From 823b7add76b46bf8e7280374db6e9aefc3610ecf Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 1 May 2024 06:19:51 +0000 Subject: [PATCH 01/55] Add fastapi --- .env | 13 + tests/__init__.py => alembic.ini | 0 compose.yml | 26 +- poetry.lock | 912 ++++++++++++++++++++++++++++++- pyproject.toml | 6 +- script/format | 2 + script/lint | 3 + script/test | 3 + tad/__main__.py | 14 - tad/core/__init__.py | 0 tad/core/config.py | 81 +++ tad/main.py | 19 + tad/tests/__init__.py | 0 tad/tests/test_main.py | 0 tests/test_main.py | 12 - 15 files changed, 1059 insertions(+), 32 deletions(-) create mode 100644 .env rename tests/__init__.py => alembic.ini (100%) delete mode 100644 tad/__main__.py create mode 100644 tad/core/__init__.py create mode 100644 tad/core/config.py create mode 100644 tad/main.py create mode 100644 tad/tests/__init__.py create mode 100644 tad/tests/test_main.py delete mode 100644 tests/test_main.py diff --git a/.env b/.env new file mode 100644 index 00000000..7dd38f56 --- /dev/null +++ b/.env @@ -0,0 +1,13 @@ +# Domain +DOMAIN=localhost + +# Environment: local, staging, production +ENVIRONMENT=local + +PROJECT_NAME="TAD" + +# Backend +BACKEND_CORS_ORIGINS="http://localhost,https://localhost" +SECRET_KEY=changethis + +POSTGRES_PASSWORD=changethis diff --git a/tests/__init__.py b/alembic.ini similarity index 100% rename from tests/__init__.py rename to alembic.ini diff --git a/compose.yml b/compose.yml index 98f470a8..07491591 100644 --- a/compose.yml +++ b/compose.yml @@ -1,4 +1,28 @@ services: tad: - build: . + build: + context: ./tad image: ghcr.io/minbzk/tad:latest + restart: unless-stopped + depends_on: + - db + env_file: + - .env + + db: + image: postgres:12 + restart: unless-stopped + volumes: + - app-db-data:/var/lib/postgresql/data/pgdata + env_file: + - .env + environment: + - PGDATA=/var/lib/postgresql/data/pgdata + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set} + - POSTGRES_USER=${POSTGRES_USER?Variable not set} + - POSTGRES_DB=${POSTGRES_DB?Variable not set} + +#TODO(berry): Traefik + +volumes: + app-db-data: diff --git a/poetry.lock b/poetry.lock index d45d09f5..b41861d8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,38 @@ # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + [[package]] name = "arrow" version = "1.3.0" @@ -49,6 +82,17 @@ files = [ {file = "boolean.py-4.0.tar.gz", hash = "sha256:17b9a181630e43dde1851d42bef546d616d5d9b4480357514597e78b203d06e4"}, ] +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + [[package]] name = "cfgv" version = "3.4.0" @@ -71,6 +115,20 @@ files = [ {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, ] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -211,6 +269,41 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.1.1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, + {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + [[package]] name = "exceptiongroup" version = "1.2.1" @@ -225,6 +318,36 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "fastapi" +version = "0.110.3" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"}, + {file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"}, +] + +[package.dependencies] +email_validator = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} +httpx = {version = ">=0.23.0", optional = true, markers = "extra == \"all\""} +itsdangerous = {version = ">=1.1.0", optional = true, markers = "extra == \"all\""} +jinja2 = {version = ">=2.11.2", optional = true, markers = "extra == \"all\""} +orjson = {version = ">=3.2.1", optional = true, markers = "extra == \"all\""} +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +pydantic-extra-types = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} +pydantic-settings = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} +python-multipart = {version = ">=0.0.7", optional = true, markers = "extra == \"all\""} +pyyaml = {version = ">=5.3.1", optional = true, markers = "extra == \"all\""} +starlette = ">=0.37.2,<0.38.0" +typing-extensions = ">=4.8.0" +ujson = {version = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0", optional = true, markers = "extra == \"all\""} +uvicorn = {version = ">=0.12.0", extras = ["standard"], optional = true, markers = "extra == \"all\""} + +[package.extras] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + [[package]] name = "filelock" version = "3.13.4" @@ -252,6 +375,110 @@ files = [ {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, ] +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + [[package]] name = "identify" version = "2.5.36" @@ -302,6 +529,34 @@ files = [ [package.dependencies] arrow = ">=0.15.0" +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + [[package]] name = "jsonpointer" version = "2.4" @@ -473,7 +728,6 @@ files = [ {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:9e2addd2d1866fe112bc6f80117bcc6bc25191c5ed1bfbcf9f1386a884252ae8"}, {file = "lxml-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:f51969bac61441fd31f028d7b3b45962f3ecebf691a510495e5d2cd8c8092dbd"}, {file = "lxml-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b0b58fbfa1bf7367dde8a557994e3b1637294be6cf2169810375caf8571a085c"}, - {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5"}, {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:804f74efe22b6a227306dd890eecc4f8c59ff25ca35f1f14e7482bbce96ef10b"}, {file = "lxml-5.2.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08802f0c56ed150cc6885ae0788a321b73505d2263ee56dad84d200cab11c07a"}, {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8c09ed18ecb4ebf23e02b8e7a22a05d6411911e6fabef3a36e4f371f4f2585"}, @@ -545,6 +799,75 @@ html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] source = ["Cython (>=3.0.10)"] +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + [[package]] name = "nodeenv" version = "1.8.0" @@ -559,6 +882,66 @@ files = [ [package.dependencies] setuptools = "*" +[[package]] +name = "orjson" +version = "3.10.1" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.1-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8ec2fc456d53ea4a47768f622bb709be68acd455b0c6be57e91462259741c4f3"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e900863691d327758be14e2a491931605bd0aded3a21beb6ce133889830b659"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab6ecbd6fe57785ebc86ee49e183f37d45f91b46fc601380c67c5c5e9c0014a2"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af7c68b01b876335cccfb4eee0beef2b5b6eae1945d46a09a7c24c9faac7a77"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:915abfb2e528677b488a06eba173e9d7706a20fdfe9cdb15890b74ef9791b85e"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3fd4a36eff9c63d25503b439531d21828da9def0059c4f472e3845a081aa0b"}, + {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d229564e72cfc062e6481a91977a5165c5a0fdce11ddc19ced8471847a67c517"}, + {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9e00495b18304173ac843b5c5fbea7b6f7968564d0d49bef06bfaeca4b656f4e"}, + {file = "orjson-3.10.1-cp310-none-win32.whl", hash = "sha256:fd78ec55179545c108174ba19c1795ced548d6cac4d80d014163033c047ca4ea"}, + {file = "orjson-3.10.1-cp310-none-win_amd64.whl", hash = "sha256:50ca42b40d5a442a9e22eece8cf42ba3d7cd4cd0f2f20184b4d7682894f05eec"}, + {file = "orjson-3.10.1-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b345a3d6953628df2f42502297f6c1e1b475cfbf6268013c94c5ac80e8abc04c"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caa7395ef51af4190d2c70a364e2f42138e0e5fcb4bc08bc9b76997659b27dab"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b01d701decd75ae092e5f36f7b88a1e7a1d3bb7c9b9d7694de850fb155578d5a"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5028981ba393f443d8fed9049211b979cadc9d0afecf162832f5a5b152c6297"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31ff6a222ea362b87bf21ff619598a4dc1106aaafaea32b1c4876d692891ec27"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e852a83d7803d3406135fb7a57cf0c1e4a3e73bac80ec621bd32f01c653849c5"}, + {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2567bc928ed3c3fcd90998009e8835de7c7dc59aabcf764b8374d36044864f3b"}, + {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4ce98cac60b7bb56457bdd2ed7f0d5d7f242d291fdc0ca566c83fa721b52e92d"}, + {file = "orjson-3.10.1-cp311-none-win32.whl", hash = "sha256:813905e111318acb356bb8029014c77b4c647f8b03f314e7b475bd9ce6d1a8ce"}, + {file = "orjson-3.10.1-cp311-none-win_amd64.whl", hash = "sha256:03a3ca0b3ed52bed1a869163a4284e8a7b0be6a0359d521e467cdef7e8e8a3ee"}, + {file = "orjson-3.10.1-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f02c06cee680b1b3a8727ec26c36f4b3c0c9e2b26339d64471034d16f74f4ef5"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1aa2f127ac546e123283e437cc90b5ecce754a22306c7700b11035dad4ccf85"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2cf29b4b74f585225196944dffdebd549ad2af6da9e80db7115984103fb18a96"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1b130c20b116f413caf6059c651ad32215c28500dce9cd029a334a2d84aa66f"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d31f9a709e6114492136e87c7c6da5e21dfedebefa03af85f3ad72656c493ae9"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d1d169461726f271ab31633cf0e7e7353417e16fb69256a4f8ecb3246a78d6e"}, + {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57c294d73825c6b7f30d11c9e5900cfec9a814893af7f14efbe06b8d0f25fba9"}, + {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7f11dbacfa9265ec76b4019efffabaabba7a7ebf14078f6b4df9b51c3c9a8ea"}, + {file = "orjson-3.10.1-cp312-none-win32.whl", hash = "sha256:d89e5ed68593226c31c76ab4de3e0d35c760bfd3fbf0a74c4b2be1383a1bf123"}, + {file = "orjson-3.10.1-cp312-none-win_amd64.whl", hash = "sha256:aa76c4fe147fd162107ce1692c39f7189180cfd3a27cfbc2ab5643422812da8e"}, + {file = "orjson-3.10.1-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a2c6a85c92d0e494c1ae117befc93cf8e7bca2075f7fe52e32698da650b2c6d1"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9813f43da955197d36a7365eb99bed42b83680801729ab2487fef305b9ced866"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec917b768e2b34b7084cb6c68941f6de5812cc26c6f1a9fecb728e36a3deb9e8"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5252146b3172d75c8a6d27ebca59c9ee066ffc5a277050ccec24821e68742fdf"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:536429bb02791a199d976118b95014ad66f74c58b7644d21061c54ad284e00f4"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dfed3c3e9b9199fb9c3355b9c7e4649b65f639e50ddf50efdf86b45c6de04b5"}, + {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2b230ec35f188f003f5b543644ae486b2998f6afa74ee3a98fc8ed2e45960afc"}, + {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:01234249ba19c6ab1eb0b8be89f13ea21218b2d72d496ef085cfd37e1bae9dd8"}, + {file = "orjson-3.10.1-cp38-none-win32.whl", hash = "sha256:8a884fbf81a3cc22d264ba780920d4885442144e6acaa1411921260416ac9a54"}, + {file = "orjson-3.10.1-cp38-none-win_amd64.whl", hash = "sha256:dab5f802d52b182163f307d2b1f727d30b1762e1923c64c9c56dd853f9671a49"}, + {file = "orjson-3.10.1-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a51fd55d4486bc5293b7a400f9acd55a2dc3b5fc8420d5ffe9b1d6bb1a056a5e"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53521542a6db1411b3bfa1b24ddce18605a3abdc95a28a67b33f9145f26aa8f2"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:27d610df96ac18ace4931411d489637d20ab3b8f63562b0531bba16011998db0"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79244b1456e5846d44e9846534bd9e3206712936d026ea8e6a55a7374d2c0694"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d751efaa8a49ae15cbebdda747a62a9ae521126e396fda8143858419f3b03610"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27ff69c620a4fff33267df70cfd21e0097c2a14216e72943bd5414943e376d77"}, + {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ebc58693464146506fde0c4eb1216ff6d4e40213e61f7d40e2f0dde9b2f21650"}, + {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5be608c3972ed902e0143a5b8776d81ac1059436915d42defe5c6ae97b3137a4"}, + {file = "orjson-3.10.1-cp39-none-win32.whl", hash = "sha256:4ae10753e7511d359405aadcbf96556c86e9dbf3a948d26c2c9f9a150c52b091"}, + {file = "orjson-3.10.1-cp39-none-win_amd64.whl", hash = "sha256:fb5bc4caa2c192077fdb02dce4e5ef8639e7f20bec4e3a834346693907362932"}, + {file = "orjson-3.10.1.tar.gz", hash = "sha256:a883b28d73370df23ed995c466b4f6c708c1f7a9bdc400fe89165c96c7603204"}, +] + [[package]] name = "packageurl-python" version = "0.15.0" @@ -703,6 +1086,152 @@ files = [ [package.dependencies] defusedxml = ">=0.7.1,<0.8.0" +[[package]] +name = "pydantic" +version = "2.7.1" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, + {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.2" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, + {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, + {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, + {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, + {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, + {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, + {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, + {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, + {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, + {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, + {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, + {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, + {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, + {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-extra-types" +version = "2.7.0" +description = "Extra Pydantic types." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_extra_types-2.7.0-py3-none-any.whl", hash = "sha256:ac01bbdaa4f85e4c4744a9792c5b0cfe61efa5028a0e670c3d8bfbf8b36c8543"}, + {file = "pydantic_extra_types-2.7.0.tar.gz", hash = "sha256:b9d9ddd755fa5960ec5a77cffcbd5d8796a0116e1dfc8f7c3a27fa0041693382"}, +] + +[package.dependencies] +pydantic = ">=2.5.2" + +[package.extras] +all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23)", "python-ulid (>=1,<2)", "python-ulid (>=1,<3)"] + +[[package]] +name = "pydantic-settings" +version = "2.2.1" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, + {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, +] + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" + +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + [[package]] name = "pyparsing" version = "3.1.2" @@ -771,6 +1300,34 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + [[package]] name = "pyyaml" version = "6.0.1" @@ -1032,6 +1589,17 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + [[package]] name = "sortedcontainers" version = "2.4.0" @@ -1043,6 +1611,23 @@ files = [ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] +[[package]] +name = "starlette" +version = "0.37.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + [[package]] name = "tomli" version = "2.0.1" @@ -1065,6 +1650,91 @@ files = [ {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, ] +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "ujson" +version = "5.9.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, + {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, + {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, + {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, + {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, + {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, + {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, + {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, + {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, + {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, + {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, + {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, + {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, +] + [[package]] name = "uri-template" version = "1.3.0" @@ -1079,6 +1749,76 @@ files = [ [package.extras] dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"] +[[package]] +name = "uvicorn" +version = "0.29.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, + {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + [[package]] name = "virtualenv" version = "20.26.0" @@ -1099,6 +1839,93 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "watchfiles" +version = "0.21.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, + {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, + {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, + {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, + {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, + {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, + {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, + {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, + {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, + {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, + {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, + {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, + {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, + {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, + {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + [[package]] name = "wcwidth" version = "0.2.13" @@ -1125,7 +1952,88 @@ files = [ docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] tests = ["pytest", "pytest-cov"] +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "8774b0cb73844129d7608bf0301864a31567d89d2c602bc61eb9c28fe2aed2db" +content-hash = "7edad3200ffa1e848822f725b103ece39c9ee885d3e16c40bd333d1d2fa1a3b2" diff --git a/pyproject.toml b/pyproject.toml index a4391799..c9b3693a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ packages = [ [tool.poetry.dependencies] python = "^3.10" +fastapi = {extras = ["all"], version = "^0.110.3"} [tool.poetry.group.test.dependencies] pytest = "^8.1.1" @@ -59,7 +60,7 @@ fixable = ["ALL"] task-tags = ["TODO"] [tool.ruff.lint.per-file-ignores] -"tests/**.py" = ["S101"] +"tad/tests/**.py" = ["S101"] [tool.ruff.lint.mccabe] max-complexity = 8 @@ -72,10 +73,9 @@ reportMissingTypeStubs = true [tool.coverage.run] branch = true -command_line = "-m pytest ./tests" +command_line = "-m pytest" relative_files = true - [tool.coverage.report] fail_under = 95 diff --git a/script/format b/script/format index a0297e82..00fd706f 100755 --- a/script/format +++ b/script/format @@ -1,3 +1,5 @@ #!/usr/bin/env bash +set -x + ruff format $@ diff --git a/script/lint b/script/lint index a993d18a..971b7902 100755 --- a/script/lint +++ b/script/lint @@ -1,3 +1,6 @@ #!/usr/bin/env bash +set -e +set -x + ruff check --fix $@ diff --git a/script/test b/script/test index 345a13a6..f88c0bea 100755 --- a/script/test +++ b/script/test @@ -1,5 +1,8 @@ #!/usr/bin/env bash +set -e +set -x + coverage run -m pytest $@ if [ $? -ne 0 ]; then echo "Test failed" diff --git a/tad/__main__.py b/tad/__main__.py deleted file mode 100644 index 3cc1e6fa..00000000 --- a/tad/__main__.py +++ /dev/null @@ -1,14 +0,0 @@ -import sys - - -def main() -> int: - # implement python validation - - # implement argument parsing - - # read config directory - return 0 - - -if __name__ == "__main__": # pragma: no cover - sys.exit(main()) diff --git a/tad/core/__init__.py b/tad/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tad/core/config.py b/tad/core/config.py new file mode 100644 index 00000000..7d1311e9 --- /dev/null +++ b/tad/core/config.py @@ -0,0 +1,81 @@ +import secrets +import warnings +from typing import Annotated, Any, Literal, Self + +from pydantic import ( + AnyUrl, + BeforeValidator, + PostgresDsn, + computed_field, + model_validator, +) +from pydantic_core import MultiHostUrl +from pydantic_settings import BaseSettings, SettingsConfigDict + + +def parse_cors(v: Any) -> list[str] | str: + if isinstance(v, str) and not v.startswith("["): + return [i.strip() for i in v.split(",")] + elif isinstance(v, list | str): + return v + raise ValueError(v) + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file=".env", env_ignore_empty=True, extra="ignore") + API_V1_STR: str = "/api/v1" + SECRET_KEY: str = secrets.token_urlsafe(32) + + DOMAIN: str = "localhost" + ENVIRONMENT: Literal["local", "staging", "production"] = "local" + + @computed_field # type: ignore[misc] + @property + def server_host(self) -> str: + # Use HTTPS for anything other than local development + if self.ENVIRONMENT == "local": + return f"http://{self.DOMAIN}" + return f"https://{self.DOMAIN}" + + BACKEND_CORS_ORIGINS: Annotated[list[AnyUrl] | str, BeforeValidator(parse_cors)] = [] + + PROJECT_NAME: str = "TAD" + + SQLALCHEMY_SCHEME: str = "postgresql+psycopg" + POSTGRES_SERVER: str = "db" + POSTGRES_PORT: int = 5432 + POSTGRES_USER: str = "postgres" + POSTGRES_PASSWORD: str + POSTGRES_DB: str = "tad" + + @computed_field # type: ignore[misc] + @property + def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn: + return MultiHostUrl.build( + scheme=self.SQLALCHEMY_SCHEME, + username=self.POSTGRES_USER, + password=self.POSTGRES_PASSWORD, + host=self.POSTGRES_SERVER, + port=self.POSTGRES_PORT, + path=self.POSTGRES_DB, + ) + + def _check_default_secret(self, var_name: str, value: str | None) -> None: + if value == "changethis": + message = ( + f'The value of {var_name} is "changethis", ' "for security, please change it, at least for deployments." + ) + if self.ENVIRONMENT == "local": + warnings.warn(message, stacklevel=1) + else: + raise ValueError(message) + + @model_validator(mode="after") + def _enforce_non_default_secrets(self) -> Self: + self._check_default_secret("SECRET_KEY", self.SECRET_KEY) + self._check_default_secret("POSTGRES_PASSWORD", self.POSTGRES_PASSWORD) + + return self + + +settings = Settings() # type: ignore diff --git a/tad/main.py b/tad/main.py new file mode 100644 index 00000000..e7f0a1ff --- /dev/null +++ b/tad/main.py @@ -0,0 +1,19 @@ +from app.core.config import settings +from fastapi import FastAPI +from starlette.middleware.cors import CORSMiddleware + +app = FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json") + +if settings.BACKEND_CORS_ORIGINS: + app.add_middleware( + CORSMiddleware, + allow_origins=[str(origin).strip("/") for origin in settings.BACKEND_CORS_ORIGINS], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + +@app.get("/") +async def root(): + return {"message": "Hello World"} diff --git a/tad/tests/__init__.py b/tad/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tad/tests/test_main.py b/tad/tests/test_main.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_main.py b/tests/test_main.py deleted file mode 100644 index 4ea66ccc..00000000 --- a/tests/test_main.py +++ /dev/null @@ -1,12 +0,0 @@ -import subprocess - -from tad import __main__ - - -def test_main_process(): - completed_process = subprocess.run(args=["-m", "tad"], executable="python", capture_output=True, text=True) - assert completed_process.returncode == 0 - - -def test_main_function(): - assert __main__.main() == 0 From 2369fcd0fe663463d0276213650717e73725aee2 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 1 May 2024 13:30:20 +0000 Subject: [PATCH 02/55] Setup sqlmodel --- .env | 9 +- .vscode/extensions.json | 2 +- .vscode/launch.json | 10 +- .vscode/tasks.json | 2 +- Dockerfile | 13 ++- compose.override.yml | 4 - compose.yml | 5 +- poetry.lock | 209 +++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 + tad/core/config.py | 25 ++++- tad/core/db.py | 10 ++ tad/main.py | 19 +++- tad/models.py | 8 ++ 13 files changed, 293 insertions(+), 25 deletions(-) delete mode 100644 compose.override.yml create mode 100644 tad/core/db.py create mode 100644 tad/models.py diff --git a/.env b/.env index 7dd38f56..6104e974 100644 --- a/.env +++ b/.env @@ -7,7 +7,10 @@ ENVIRONMENT=local PROJECT_NAME="TAD" # Backend -BACKEND_CORS_ORIGINS="http://localhost,https://localhost" -SECRET_KEY=changethis +BACKEND_CORS_ORIGINS="http://localhost,https://localhost,http://127.0.0.1,https://127.0.0.1" +SECRET_KEY=changethisa -POSTGRES_PASSWORD=changethis +POSTGRES_PORT=5432 +POSTGRES_DB=app +POSTGRES_USER=postgres +POSTGRES_PASSWORD=changethisa diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 86852c65..4b7ea0f2 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,6 +5,6 @@ "streetsidesoftware.code-spell-checker", "redhat.vscode-yaml", "charliermarsh.ruff", - "github.vscode-github-actions" + "github.vscode-github-actions", ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index a1fb3b63..caded2c2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,12 +2,16 @@ "version": "0.2.0", "configurations": [ { - "name": "tad", + "name": "tad - fastapi", "type": "debugpy", "request": "launch", - "module": "tad", + "module": "uvicorn", "justMyCode": false, - "args": [] + "args": ["main:app"], + "cwd": "${workspaceFolder}/tad/", + "env": { + "PYTHONPATH": "${workspaceFolder}" + } }, { "name": "Project: tests", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 99757d7a..aaf2eb83 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -41,7 +41,7 @@ "label": "PyTest", "detail": "test all code", "type": "shell", - "command": "coverage run -m pytest ./tests", + "command": "coverage run -m pytest ./tad/tests", "group": { "kind": "test", "isDefault": true diff --git a/Dockerfile b/Dockerfile index 1db7f339..6f16c49a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,8 @@ ARG PYTHON_VERSION=3.11.7-slim FROM --platform=$BUILDPLATFORM python:${PYTHON_VERSION} AS project-base LABEL maintainer=ai-validatie@minbzk.nl \ - organization=MinBZK \ - license=EUPL-1.2 + organization=MinBZK \ + license=EUPL-1.2 ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ @@ -51,5 +51,12 @@ USER tad COPY --chown=root:root --chmod=755 ./tad /app/tad +EXPOSE 8000 +RUN poetry install + +ENV PYTHONPATH=/app/ +WORKDIR /app/tad/ + # change this to a usefull command -CMD ["python", "-m", "tad" ] +CMD ["python", "-m", "uvicorn", "main:app" ] + diff --git a/compose.override.yml b/compose.override.yml deleted file mode 100644 index e6485dd5..00000000 --- a/compose.override.yml +++ /dev/null @@ -1,4 +0,0 @@ -services: - tad: - build: . - image: ghcr.io/minbzk/tad:dev diff --git a/compose.yml b/compose.yml index 07491591..4c592765 100644 --- a/compose.yml +++ b/compose.yml @@ -1,13 +1,14 @@ services: tad: - build: - context: ./tad + build: . image: ghcr.io/minbzk/tad:latest restart: unless-stopped depends_on: - db env_file: - .env + ports: + - 8001:8000 db: image: postgres:12 diff --git a/poetry.lock b/poetry.lock index b41861d8..37b35113 100644 --- a/poetry.lock +++ b/poetry.lock @@ -375,6 +375,77 @@ files = [ {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, ] +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + [[package]] name = "h11" version = "0.14.0" @@ -1072,6 +1143,29 @@ wcwidth = "*" [package.extras] tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"] +[[package]] +name = "psycopg" +version = "3.1.18" +description = "PostgreSQL database adapter for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg-3.1.18-py3-none-any.whl", hash = "sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e"}, + {file = "psycopg-3.1.18.tar.gz", hash = "sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b"}, +] + +[package.dependencies] +typing-extensions = ">=4.1" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +binary = ["psycopg-binary (==3.1.18)"] +c = ["psycopg-c (==3.1.18)"] +dev = ["black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] +docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] +pool = ["psycopg-pool"] +test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] + [[package]] name = "py-serializable" version = "1.0.3" @@ -1611,6 +1705,108 @@ files = [ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] +[[package]] +name = "sqlalchemy" +version = "2.0.29" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c142852ae192e9fe5aad5c350ea6befe9db14370b34047e1f0f7cf99e63c63b"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a1e69d4e26f71e750e9ad6fdc8614fbddb67cfe2173a3628a2566034e223c7"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef3fbccb4058355053c51b82fd3501a6e13dd808c8d8cd2561e610c5456013c"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6753305936eddc8ed190e006b7bb33a8f50b9854823485eed3a886857ab8d1"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0f3ca96af060a5250a8ad5a63699180bc780c2edf8abf96c58af175921df847a"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4520047006b1d3f0d89e0532978c0688219857eb2fee7c48052560ae76aca1e"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-win32.whl", hash = "sha256:b2a0e3cf0caac2085ff172c3faacd1e00c376e6884b5bc4dd5b6b84623e29e4f"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-win_amd64.whl", hash = "sha256:01d10638a37460616708062a40c7b55f73e4d35eaa146781c683e0fa7f6c43fb"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:308ef9cb41d099099fffc9d35781638986870b29f744382904bf9c7dadd08513"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:296195df68326a48385e7a96e877bc19aa210e485fa381c5246bc0234c36c78e"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a13b917b4ffe5a0a31b83d051d60477819ddf18276852ea68037a144a506efb9"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f6d971255d9ddbd3189e2e79d743ff4845c07f0633adfd1de3f63d930dbe673"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61405ea2d563407d316c63a7b5271ae5d274a2a9fbcd01b0aa5503635699fa1e"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de7202ffe4d4a8c1e3cde1c03e01c1a3772c92858837e8f3879b497158e4cb44"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-win32.whl", hash = "sha256:b5d7ed79df55a731749ce65ec20d666d82b185fa4898430b17cb90c892741520"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-win_amd64.whl", hash = "sha256:205f5a2b39d7c380cbc3b5dcc8f2762fb5bcb716838e2d26ccbc54330775b003"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d96710d834a6fb31e21381c6d7b76ec729bd08c75a25a5184b1089141356171f"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52de4736404e53c5c6a91ef2698c01e52333988ebdc218f14c833237a0804f1b"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c7b02525ede2a164c5fa5014915ba3591730f2cc831f5be9ff3b7fd3e30958e"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dfefdb3e54cd15f5d56fd5ae32f1da2d95d78319c1f6dfb9bcd0eb15d603d5d"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a88913000da9205b13f6f195f0813b6ffd8a0c0c2bd58d499e00a30eb508870c"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fecd5089c4be1bcc37c35e9aa678938d2888845a134dd016de457b942cf5a758"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-win32.whl", hash = "sha256:8197d6f7a3d2b468861ebb4c9f998b9df9e358d6e1cf9c2a01061cb9b6cf4e41"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-win_amd64.whl", hash = "sha256:9b19836ccca0d321e237560e475fd99c3d8655d03da80c845c4da20dda31b6e1"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87a1d53a5382cdbbf4b7619f107cc862c1b0a4feb29000922db72e5a66a5ffc0"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0732dffe32333211801b28339d2a0babc1971bc90a983e3035e7b0d6f06b93"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90453597a753322d6aa770c5935887ab1fc49cc4c4fdd436901308383d698b4b"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ea311d4ee9a8fa67f139c088ae9f905fcf0277d6cd75c310a21a88bf85e130f5"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5f20cb0a63a3e0ec4e169aa8890e32b949c8145983afa13a708bc4b0a1f30e03"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-win32.whl", hash = "sha256:e5bbe55e8552019c6463709b39634a5fc55e080d0827e2a3a11e18eb73f5cdbd"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-win_amd64.whl", hash = "sha256:c2f9c762a2735600654c654bf48dad388b888f8ce387b095806480e6e4ff6907"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e614d7a25a43a9f54fcce4675c12761b248547f3d41b195e8010ca7297c369c"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:471fcb39c6adf37f820350c28aac4a7df9d3940c6548b624a642852e727ea586"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:988569c8732f54ad3234cf9c561364221a9e943b78dc7a4aaf35ccc2265f1930"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dddaae9b81c88083e6437de95c41e86823d150f4ee94bf24e158a4526cbead01"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:334184d1ab8f4c87f9652b048af3f7abea1c809dfe526fb0435348a6fef3d380"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:38b624e5cf02a69b113c8047cf7f66b5dfe4a2ca07ff8b8716da4f1b3ae81567"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-win32.whl", hash = "sha256:bab41acf151cd68bc2b466deae5deeb9e8ae9c50ad113444151ad965d5bf685b"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-win_amd64.whl", hash = "sha256:52c8011088305476691b8750c60e03b87910a123cfd9ad48576d6414b6ec2a1d"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3071ad498896907a5ef756206b9dc750f8e57352113c19272bdfdc429c7bd7de"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dba622396a3170974f81bad49aacebd243455ec3cc70615aeaef9e9613b5bca5"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b184e3de58009cc0bf32e20f137f1ec75a32470f5fede06c58f6c355ed42a72"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c37f1050feb91f3d6c32f864d8e114ff5545a4a7afe56778d76a9aec62638ba"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bda7ce59b06d0f09afe22c56714c65c957b1068dee3d5e74d743edec7daba552"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25664e18bef6dc45015b08f99c63952a53a0a61f61f2e48a9e70cec27e55f699"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-win32.whl", hash = "sha256:77d29cb6c34b14af8a484e831ab530c0f7188f8efed1c6a833a2c674bf3c26ec"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-win_amd64.whl", hash = "sha256:04c487305ab035a9548f573763915189fc0fe0824d9ba28433196f8436f1449c"}, + {file = "SQLAlchemy-2.0.29-py3-none-any.whl", hash = "sha256:dc4ee2d4ee43251905f88637d5281a8d52e916a021384ec10758826f5cbae305"}, + {file = "SQLAlchemy-2.0.29.tar.gz", hash = "sha256:bd9566b8e58cabd700bc367b60e90d9349cd16f0984973f98a9a09f9c64e86f0"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlmodel" +version = "0.0.18" +description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sqlmodel-0.0.18-py3-none-any.whl", hash = "sha256:d70fdf8fe595e30a918660cf4537b9c5fc2fffdbfcba851a0135de73c3ebcbb7"}, + {file = "sqlmodel-0.0.18.tar.gz", hash = "sha256:2e520efe03810ef2c268a1004cfc5ef8f8a936312232f38d6c8e62c11af2cac3"}, +] + +[package.dependencies] +pydantic = ">=1.10.13,<3.0.0" +SQLAlchemy = ">=2.0.0,<2.1.0" + [[package]] name = "starlette" version = "0.37.2" @@ -1661,6 +1857,17 @@ files = [ {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + [[package]] name = "ujson" version = "5.9.0" @@ -2036,4 +2243,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "7edad3200ffa1e848822f725b103ece39c9ee885d3e16c40bd333d1d2fa1a3b2" +content-hash = "0823e1b2d159fbe0c0ebe19e398fee382b19b3f6ef6fda52572612c9d736181e" diff --git a/pyproject.toml b/pyproject.toml index c9b3693a..c718194f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,8 @@ packages = [ [tool.poetry.dependencies] python = "^3.10" fastapi = {extras = ["all"], version = "^0.110.3"} +sqlmodel = "^0.0.18" +psycopg = "^3.1.18" [tool.poetry.group.test.dependencies] pytest = "^8.1.1" diff --git a/tad/core/config.py b/tad/core/config.py index 7d1311e9..f5858498 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -41,24 +41,41 @@ def server_host(self) -> str: PROJECT_NAME: str = "TAD" - SQLALCHEMY_SCHEME: str = "postgresql+psycopg" + #SQLALCHEMY_SCHEME: str = "postgresql+psycopg" + SQLALCHEMY_SCHEME: str = "sqlite" + POSTGRES_SERVER: str = "db" POSTGRES_PORT: int = 5432 POSTGRES_USER: str = "postgres" POSTGRES_PASSWORD: str POSTGRES_DB: str = "tad" + SQLITE_FILE: str = "/tmp/database" + + @computed_field # type: ignore[misc] @property - def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn: - return MultiHostUrl.build( + def SQLALCHEMY_DATABASE_URI(self) -> str: + + if self.SQLALCHEMY_SCHEME == "sqlite": + return str(MultiHostUrl.build( + scheme=self.SQLALCHEMY_SCHEME, + host="", + path=self.SQLITE_FILE + )) + + + return str(MultiHostUrl.build( scheme=self.SQLALCHEMY_SCHEME, username=self.POSTGRES_USER, password=self.POSTGRES_PASSWORD, host=self.POSTGRES_SERVER, port=self.POSTGRES_PORT, path=self.POSTGRES_DB, - ) + )) + + + def _check_default_secret(self, var_name: str, value: str | None) -> None: if value == "changethis": diff --git a/tad/core/db.py b/tad/core/db.py new file mode 100644 index 00000000..44267351 --- /dev/null +++ b/tad/core/db.py @@ -0,0 +1,10 @@ +from sqlmodel import SQLModel, create_engine + +from tad.core.config import settings + +engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, echo=True) + +def init_db() -> None: + SQLModel.metadata.create_all(engine) + + diff --git a/tad/main.py b/tad/main.py index e7f0a1ff..5362d039 100644 --- a/tad/main.py +++ b/tad/main.py @@ -1,8 +1,16 @@ -from app.core.config import settings +import logging + from fastapi import FastAPI +from sqlmodel import Session, select from starlette.middleware.cors import CORSMiddleware -app = FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json") +from tad.core.config import settings +from tad.core.db import engine, init_db +from tad.models import Hero + +logger = logging.getLogger(__name__) + +app = FastAPI(title=settings.PROJECT_NAME) if settings.BACKEND_CORS_ORIGINS: app.add_middleware( @@ -13,7 +21,12 @@ allow_headers=["*"], ) - @app.get("/") async def root(): + init_db() + # with Session(engine) as session: + # statement = select(UserBase).where(UserBase.full_name == "Spider-Boy") + # hero = session.exec(statement).first() + # print(hero) + return {"message": "Hello World"} diff --git a/tad/models.py b/tad/models.py new file mode 100644 index 00000000..88a07d81 --- /dev/null +++ b/tad/models.py @@ -0,0 +1,8 @@ +from sqlmodel import Field, SQLModel + + +class Hero(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str + secret_name: str + age: int | None = None From 9bd890e231f4d58d5b86604f1391479c9a7023d3 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Thu, 2 May 2024 14:07:25 +0200 Subject: [PATCH 03/55] Add alembic --- Dockerfile | 1 - alembic.ini | 116 ++++++++++++++++++++++++++ poetry.lock | 40 ++++++++- pyproject.toml | 1 + tad/alembic/env.py | 94 +++++++++++++++++++++ tad/alembic/script.py.mako | 27 ++++++ tad/alembic/versions/99fb931b1324_.py | 49 +++++++++++ tad/core/config.py | 32 +++---- tad/core/db.py | 3 +- tad/main.py | 1 + tad/models.py | 13 ++- 11 files changed, 352 insertions(+), 25 deletions(-) create mode 100644 tad/alembic/env.py create mode 100644 tad/alembic/script.py.mako create mode 100644 tad/alembic/versions/99fb931b1324_.py diff --git a/Dockerfile b/Dockerfile index 6f16c49a..98120552 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,4 +59,3 @@ WORKDIR /app/tad/ # change this to a usefull command CMD ["python", "-m", "uvicorn", "main:app" ] - diff --git a/alembic.ini b/alembic.ini index e69de29b..b4f7fe01 100644 --- a/alembic.ini +++ b/alembic.ini @@ -0,0 +1,116 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = tad/alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +# prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +# version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/poetry.lock b/poetry.lock index 37b35113..fae51db5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,24 @@ # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +[[package]] +name = "alembic" +version = "1.13.1" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + [[package]] name = "annotated-types" version = "0.6.0" @@ -870,6 +889,25 @@ html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] source = ["Cython (>=3.0.10)"] +[[package]] +name = "mako" +version = "1.3.3" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.3-py3-none-any.whl", hash = "sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40"}, + {file = "Mako-1.3.3.tar.gz", hash = "sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + [[package]] name = "markupsafe" version = "2.1.5" @@ -2243,4 +2281,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "0823e1b2d159fbe0c0ebe19e398fee382b19b3f6ef6fda52572612c9d736181e" +content-hash = "49f1eb7773057c7c8e5ae1439589ac95e077fa39389131a9e691e16d95cd9848" diff --git a/pyproject.toml b/pyproject.toml index c718194f..e27fb326 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ python = "^3.10" fastapi = {extras = ["all"], version = "^0.110.3"} sqlmodel = "^0.0.18" psycopg = "^3.1.18" +alembic = "^1.13.1" [tool.poetry.group.test.dependencies] pytest = "^8.1.1" diff --git a/tad/alembic/env.py b/tad/alembic/env.py new file mode 100644 index 00000000..084a9c0d --- /dev/null +++ b/tad/alembic/env.py @@ -0,0 +1,94 @@ +import os +from logging.config import fileConfig + +from sqlalchemy import engine_from_config, pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +from tad.models import SQLModel # noqa + +target_metadata = SQLModel.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def get_url(): + scheme=os.getenv("SQLALCHEMY_SCHEME", "sqlite") + + if scheme == "sqlite": + file = os.getenv("SQLITE_FILE", "/tmp/database") + return f"{scheme}:///{file}" + + + user = os.getenv("POSTGRES_USER", "postgres") + password = os.getenv("POSTGRES_PASSWORD", "") + server = os.getenv("POSTGRES_SERVER", "db") + port = os.getenv("POSTGRES_PORT", "5432") + db = os.getenv("POSTGRES_DB", "tad") + return f"{scheme}://{user}:{password}@{server}:{port}/{db}" + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = get_url() + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True, compare_type=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + configuration = config.get_section(config.config_ini_section) + configuration["sqlalchemy.url"] = get_url() + connectable = engine_from_config( + configuration, + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata, compare_type=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/tad/alembic/script.py.mako b/tad/alembic/script.py.mako new file mode 100644 index 00000000..ae4bd68b --- /dev/null +++ b/tad/alembic/script.py.mako @@ -0,0 +1,27 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/tad/alembic/versions/99fb931b1324_.py b/tad/alembic/versions/99fb931b1324_.py new file mode 100644 index 00000000..6c55fa86 --- /dev/null +++ b/tad/alembic/versions/99fb931b1324_.py @@ -0,0 +1,49 @@ +"""empty message + +Revision ID: 99fb931b1324 +Revises: +Create Date: 2024-05-02 12:04:56.694709 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes + + +# revision identifiers, used by Alembic. +revision: str = '99fb931b1324' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('hero', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('secret_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('age', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('user', + sa.Column('email', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('is_active', sa.Boolean(), nullable=False), + sa.Column('is_superuser', sa.Boolean(), nullable=False), + sa.Column('full_name', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('hashed_password', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_user_email'), table_name='user') + op.drop_table('user') + op.drop_table('hero') + # ### end Alembic commands ### diff --git a/tad/core/config.py b/tad/core/config.py index f5858498..b9ceb04f 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -41,7 +41,7 @@ def server_host(self) -> str: PROJECT_NAME: str = "TAD" - #SQLALCHEMY_SCHEME: str = "postgresql+psycopg" + # SQLALCHEMY_SCHEME: str = "postgresql+psycopg" SQLALCHEMY_SCHEME: str = "sqlite" POSTGRES_SERVER: str = "db" @@ -52,30 +52,22 @@ def server_host(self) -> str: SQLITE_FILE: str = "/tmp/database" - @computed_field # type: ignore[misc] @property def SQLALCHEMY_DATABASE_URI(self) -> str: - if self.SQLALCHEMY_SCHEME == "sqlite": - return str(MultiHostUrl.build( - scheme=self.SQLALCHEMY_SCHEME, - host="", - path=self.SQLITE_FILE - )) - - - return str(MultiHostUrl.build( - scheme=self.SQLALCHEMY_SCHEME, - username=self.POSTGRES_USER, - password=self.POSTGRES_PASSWORD, - host=self.POSTGRES_SERVER, - port=self.POSTGRES_PORT, - path=self.POSTGRES_DB, - )) - - + return str(MultiHostUrl.build(scheme=self.SQLALCHEMY_SCHEME, host="", path=self.SQLITE_FILE)) + return str( + MultiHostUrl.build( + scheme=self.SQLALCHEMY_SCHEME, + username=self.POSTGRES_USER, + password=self.POSTGRES_PASSWORD, + host=self.POSTGRES_SERVER, + port=self.POSTGRES_PORT, + path=self.POSTGRES_DB, + ) + ) def _check_default_secret(self, var_name: str, value: str | None) -> None: if value == "changethis": diff --git a/tad/core/db.py b/tad/core/db.py index 44267351..ebfaf924 100644 --- a/tad/core/db.py +++ b/tad/core/db.py @@ -4,7 +4,6 @@ engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, echo=True) + def init_db() -> None: SQLModel.metadata.create_all(engine) - - diff --git a/tad/main.py b/tad/main.py index 5362d039..8f4630f9 100644 --- a/tad/main.py +++ b/tad/main.py @@ -21,6 +21,7 @@ allow_headers=["*"], ) + @app.get("/") async def root(): init_db() diff --git a/tad/models.py b/tad/models.py index 88a07d81..80592ea5 100644 --- a/tad/models.py +++ b/tad/models.py @@ -1,4 +1,4 @@ -from sqlmodel import Field, SQLModel +from sqlmodel import Field, Relationship, SQLModel class Hero(SQLModel, table=True): @@ -6,3 +6,14 @@ class Hero(SQLModel, table=True): name: str secret_name: str age: int | None = None + +class UserBase(SQLModel): + email: str = Field(unique=True, index=True) + is_active: bool = True + is_superuser: bool = False + full_name: str | None = None + +# Database model, database table inferred from class name +class User(UserBase, table=True): + id: int | None = Field(default=None, primary_key=True) + hashed_password: str From 3007af9ffb489ef2e738ee7f3d541ec180b97418 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Mon, 6 May 2024 11:58:18 +0000 Subject: [PATCH 04/55] Setup Alembic --- .devcontainer/devcontainer.json | 3 ++ .env | 1 + BUILD.md | 2 +- compose.yml | 11 ++++-- pyproject.toml | 3 -- Dockerfile => tad/Dockerfile | 9 ++--- alembic.ini => tad/alembic.ini | 2 +- tad/alembic/env.py | 29 +++------------ tad/alembic/versions/99fb931b1324_.py | 53 ++++++++++++++------------- tad/core/config.py | 9 +++-- tad/main.py | 9 ----- tad/models.py | 4 +- 12 files changed, 59 insertions(+), 76 deletions(-) rename Dockerfile => tad/Dockerfile (91%) rename alembic.ini => tad/alembic.ini (99%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c7e6669c..d6b217e1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,6 +7,9 @@ "POETRY_VIRTUALENVS_CREATE": "false" }, "remoteUser": "root", + "features": { + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {} + }, "postCreateCommand": ".devcontainer/postCreateCommand.sh", "customizations": { "vscode": { diff --git a/.env b/.env index 6104e974..eaead6b7 100644 --- a/.env +++ b/.env @@ -10,6 +10,7 @@ PROJECT_NAME="TAD" BACKEND_CORS_ORIGINS="http://localhost,https://localhost,http://127.0.0.1,https://127.0.0.1" SECRET_KEY=changethisa +SQLALCHEMY_SCHEME="postgresql+psycopg" POSTGRES_PORT=5432 POSTGRES_DB=app POSTGRES_USER=postgres diff --git a/BUILD.md b/BUILD.md index 8b959ace..c8caae78 100644 --- a/BUILD.md +++ b/BUILD.md @@ -20,7 +20,7 @@ poetry install When poetry is done installing all dependencies you can start using the tool. ```shell -poetry run python -m tad +poetry run python -m uvicorn tad.main:app ``` ## Building TAD with Containers diff --git a/compose.yml b/compose.yml index 4c592765..d17e16ae 100644 --- a/compose.yml +++ b/compose.yml @@ -1,14 +1,17 @@ services: tad: - build: . + build: + context: . + dockerfile: tad/Dockerfile image: ghcr.io/minbzk/tad:latest restart: unless-stopped depends_on: - - db + db: + condition: service_healthy env_file: - .env ports: - - 8001:8000 + - 8000:8000 db: image: postgres:12 @@ -22,6 +25,8 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set} - POSTGRES_USER=${POSTGRES_USER?Variable not set} - POSTGRES_DB=${POSTGRES_DB?Variable not set} + healthcheck: + test: ["CMD-SHELL", "pg_isready", "-d", "tad"] #TODO(berry): Traefik diff --git a/pyproject.toml b/pyproject.toml index e27fb326..e0291eae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,9 +38,6 @@ pyright = "^1.1.360" cyclonedx-bom = "^4.4.2" pip-licenses = "^4.4.0" -[tool.poetry.scripts] -tad = 'tad.__main__:main' - [tool.poetry.urls] "Issue Tracker" = "https://github.com/MinBZK/tad/issues" diff --git a/Dockerfile b/tad/Dockerfile similarity index 91% rename from Dockerfile rename to tad/Dockerfile index 98120552..80a54940 100644 --- a/Dockerfile +++ b/tad/Dockerfile @@ -16,7 +16,7 @@ ENV PYTHONUNBUFFERED=1 \ POETRY_HOME='/usr/local' RUN apt-get update && apt-get install -y --no-install-recommends \ - curl \ + curl libpq-dev \ && rm -rf /var/lib/apt/lists/* RUN curl -sSL https://install.python-poetry.org | python3 - @@ -51,11 +51,8 @@ USER tad COPY --chown=root:root --chmod=755 ./tad /app/tad -EXPOSE 8000 -RUN poetry install - ENV PYTHONPATH=/app/ -WORKDIR /app/tad/ +WORKDIR /app/ # change this to a usefull command -CMD ["python", "-m", "uvicorn", "main:app" ] +CMD ["python", "-m", "uvicorn", "--host", "0.0.0.0", "tad.main:app" ] diff --git a/alembic.ini b/tad/alembic.ini similarity index 99% rename from alembic.ini rename to tad/alembic.ini index b4f7fe01..c77a70cf 100644 --- a/alembic.ini +++ b/tad/alembic.ini @@ -2,7 +2,7 @@ [alembic] # path to migration scripts -script_location = tad/alembic +script_location = alembic # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s # Uncomment the line below if you want the files to be prepended with date and time diff --git a/tad/alembic/env.py b/tad/alembic/env.py index 084a9c0d..cb802bc9 100644 --- a/tad/alembic/env.py +++ b/tad/alembic/env.py @@ -5,36 +5,22 @@ from alembic import context -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. config = context.config -# Interpret the config file for Python logging. -# This line sets up loggers basically. if config.config_file_name is not None: fileConfig(config.config_file_name) -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata from tad.models import SQLModel # noqa target_metadata = SQLModel.metadata -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - def get_url(): - scheme=os.getenv("SQLALCHEMY_SCHEME", "sqlite") + scheme = os.getenv("SQLALCHEMY_SCHEME", "sqlite") if scheme == "sqlite": - file = os.getenv("SQLITE_FILE", "/tmp/database") - return f"{scheme}:///{file}" - + file = os.getenv("SQLITE_FILE", "./database") + return f"{scheme}://{file}" user = os.getenv("POSTGRES_USER", "postgres") password = os.getenv("POSTGRES_PASSWORD", "") @@ -43,6 +29,7 @@ def get_url(): db = os.getenv("POSTGRES_DB", "tad") return f"{scheme}://{user}:{password}@{server}:{port}/{db}" + def run_migrations_offline(): """Run migrations in 'offline' mode. @@ -56,9 +43,7 @@ def run_migrations_offline(): """ url = get_url() - context.configure( - url=url, target_metadata=target_metadata, literal_binds=True, compare_type=True - ) + context.configure(url=url, target_metadata=target_metadata, literal_binds=True, compare_type=True) with context.begin_transaction(): context.run_migrations() @@ -80,9 +65,7 @@ def run_migrations_online(): ) with connectable.connect() as connection: - context.configure( - connection=connection, target_metadata=target_metadata, compare_type=True - ) + context.configure(connection=connection, target_metadata=target_metadata, compare_type=True) with context.begin_transaction(): context.run_migrations() diff --git a/tad/alembic/versions/99fb931b1324_.py b/tad/alembic/versions/99fb931b1324_.py index 6c55fa86..926c4e74 100644 --- a/tad/alembic/versions/99fb931b1324_.py +++ b/tad/alembic/versions/99fb931b1324_.py @@ -1,49 +1,52 @@ """empty message Revision ID: 99fb931b1324 -Revises: +Revises: Create Date: 2024-05-02 12:04:56.694709 """ -from typing import Sequence, Union -from alembic import op +from collections.abc import Sequence + import sqlalchemy as sa import sqlmodel.sql.sqltypes +from alembic import op # revision identifiers, used by Alembic. -revision: str = '99fb931b1324' -down_revision: Union[str, None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None +revision: str = "99fb931b1324" +down_revision: str | None = None +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.create_table('hero', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('secret_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('age', sa.Integer(), nullable=True), - sa.PrimaryKeyConstraint('id') + op.create_table( + "hero", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("secret_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("age", sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint("id"), ) - op.create_table('user', - sa.Column('email', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('is_active', sa.Boolean(), nullable=False), - sa.Column('is_superuser', sa.Boolean(), nullable=False), - sa.Column('full_name', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('hashed_password', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.PrimaryKeyConstraint('id') + op.create_table( + "user", + sa.Column("email", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("is_active", sa.Boolean(), nullable=False), + sa.Column("is_superuser", sa.Boolean(), nullable=False), + sa.Column("full_name", sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("hashed_password", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint("id"), ) - op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True) + op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True) # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_user_email'), table_name='user') - op.drop_table('user') - op.drop_table('hero') + op.drop_index(op.f("ix_user_email"), table_name="user") + op.drop_table("user") + op.drop_table("hero") # ### end Alembic commands ### diff --git a/tad/core/config.py b/tad/core/config.py index b9ceb04f..f5c0f608 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -5,7 +5,6 @@ from pydantic import ( AnyUrl, BeforeValidator, - PostgresDsn, computed_field, model_validator, ) @@ -41,8 +40,10 @@ def server_host(self) -> str: PROJECT_NAME: str = "TAD" - # SQLALCHEMY_SCHEME: str = "postgresql+psycopg" - SQLALCHEMY_SCHEME: str = "sqlite" + if ENVIRONMENT == "local": + SQLALCHEMY_SCHEME: str = "sqlite" + else: + SQLALCHEMY_SCHEME: str = "postgresql+psycopg" POSTGRES_SERVER: str = "db" POSTGRES_PORT: int = 5432 @@ -50,7 +51,7 @@ def server_host(self) -> str: POSTGRES_PASSWORD: str POSTGRES_DB: str = "tad" - SQLITE_FILE: str = "/tmp/database" + SQLITE_FILE: str = "./database" @computed_field # type: ignore[misc] @property diff --git a/tad/main.py b/tad/main.py index 8f4630f9..d2246d55 100644 --- a/tad/main.py +++ b/tad/main.py @@ -1,12 +1,9 @@ import logging from fastapi import FastAPI -from sqlmodel import Session, select from starlette.middleware.cors import CORSMiddleware from tad.core.config import settings -from tad.core.db import engine, init_db -from tad.models import Hero logger = logging.getLogger(__name__) @@ -24,10 +21,4 @@ @app.get("/") async def root(): - init_db() - # with Session(engine) as session: - # statement = select(UserBase).where(UserBase.full_name == "Spider-Boy") - # hero = session.exec(statement).first() - # print(hero) - return {"message": "Hello World"} diff --git a/tad/models.py b/tad/models.py index 80592ea5..f0fd84cb 100644 --- a/tad/models.py +++ b/tad/models.py @@ -1,4 +1,4 @@ -from sqlmodel import Field, Relationship, SQLModel +from sqlmodel import Field, SQLModel class Hero(SQLModel, table=True): @@ -7,12 +7,14 @@ class Hero(SQLModel, table=True): secret_name: str age: int | None = None + class UserBase(SQLModel): email: str = Field(unique=True, index=True) is_active: bool = True is_superuser: bool = False full_name: str | None = None + # Database model, database table inferred from class name class User(UserBase, table=True): id: int | None = Field(default=None, primary_key=True) From eae3750f3d3063a018e2432c70b39b8189f42442 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Mon, 6 May 2024 13:55:45 +0000 Subject: [PATCH 05/55] Add database --- .devcontainer/devcontainer.json | 3 ++- .env | 19 +++++++++++++------ .gitattributes | 2 ++ .vscode/tasks.json | 8 ++++---- compose.yml | 28 +++++++++++++++++++++++----- database/init-user-db.sh | 9 +++++++++ pyproject.toml | 2 +- tad/alembic/env.py | 10 +++++----- tad/core/config.py | 27 ++++++++++++--------------- 9 files changed, 71 insertions(+), 37 deletions(-) create mode 100644 .gitattributes create mode 100755 database/init-user-db.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d6b217e1..596a38db 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,8 +8,9 @@ }, "remoteUser": "root", "features": { - "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {} + "ghcr.io/devcontainers/features/docker-in-docker:2": {} }, + "forwardPorts": [8000,8080], "postCreateCommand": ".devcontainer/postCreateCommand.sh", "customizations": { "vscode": { diff --git a/.env b/.env index eaead6b7..8d2b9d10 100644 --- a/.env +++ b/.env @@ -3,15 +3,22 @@ DOMAIN=localhost # Environment: local, staging, production ENVIRONMENT=local - PROJECT_NAME="TAD" -# Backend +# TAD backend BACKEND_CORS_ORIGINS="http://localhost,https://localhost,http://127.0.0.1,https://127.0.0.1" -SECRET_KEY=changethisa - +SECRET_KEY=changethis SQLALCHEMY_SCHEME="postgresql+psycopg" +APP_DATABASE_USER=tad +APP_DATABASE_DB=tad +APP_DATABASE_PASSWORD=changethis + +# Postgres database +POSTGRES_SERVER=db POSTGRES_PORT=5432 -POSTGRES_DB=app +POSTGRES_DB=postgres POSTGRES_USER=postgres -POSTGRES_PASSWORD=changethisa +POSTGRES_PASSWORD=changethis + +# Database viewer +PGADMIN_DEFAULT_PASSWORD=changethis diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..efdba876 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +*.sh text eol=lf diff --git a/.vscode/tasks.json b/.vscode/tasks.json index aaf2eb83..087db6b7 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -5,7 +5,7 @@ "label": "PyRight", "detail": "typecheck code.", "type": "shell", - "command": "pyright", + "command": "poetry run pyright", "group": "test", "presentation": { "reveal": "always", @@ -17,7 +17,7 @@ "label": "Ruff", "detail": "format and lint code.", "type": "shell", - "command": "ruff format && ruff check", + "command": "poetry run ruff format && poetry run ruff check", "group": "test", "presentation": { "reveal": "always", @@ -29,7 +29,7 @@ "label": "Code Coverage", "detail": "Generate code coverage report.", "type": "shell", - "command": "coverage report --fail-under 95", + "command": "poetry run coverage report", "group": "test", "presentation": { "reveal": "always", @@ -41,7 +41,7 @@ "label": "PyTest", "detail": "test all code", "type": "shell", - "command": "coverage run -m pytest ./tad/tests", + "command": "poetry run coverage run -m pytest", "group": { "kind": "test", "isDefault": true diff --git a/compose.yml b/compose.yml index d17e16ae..5b49484d 100644 --- a/compose.yml +++ b/compose.yml @@ -10,23 +10,41 @@ services: condition: service_healthy env_file: - .env + environment: + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Variable not set} ports: - 8000:8000 db: - image: postgres:12 + image: postgres:16 restart: unless-stopped volumes: - app-db-data:/var/lib/postgresql/data/pgdata + - ./database/:/docker-entrypoint-initdb.d/:cached env_file: - .env environment: - PGDATA=/var/lib/postgresql/data/pgdata - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set} - - POSTGRES_USER=${POSTGRES_USER?Variable not set} - - POSTGRES_DB=${POSTGRES_DB?Variable not set} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Variable not set} + - SECRET_KEY=${SECRET_KEY:?Variable not set} healthcheck: - test: ["CMD-SHELL", "pg_isready", "-d", "tad"] + test: ["CMD-SHELL", "pg_isready", "-d", "tad", "-U", "postgres"] + start_period: 10s + + + db-admin: + image: dpage/pgadmin4:8.6 + restart: unless-stopped + ports: + - 8080:8080 + environment: + - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL:-berry.hartog@minbzk.nl} + - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD:?Variable not set} + - PGADMIN_LISTEN_PORT=${PGADMIN_LISTEN_PORT:-8080} + depends_on: + db: + condition: service_healthy + #TODO(berry): Traefik diff --git a/database/init-user-db.sh b/database/init-user-db.sh new file mode 100755 index 00000000..b13ccff2 --- /dev/null +++ b/database/init-user-db.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +# todo(berry): make user and database variables +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE USER tad WITH PASSWORD 'changethis'; + CREATE DATABASE tad; + GRANT ALL PRIVILEGES ON DATABASE tad TO tad; +EOSQL diff --git a/pyproject.toml b/pyproject.toml index e0291eae..1bfd1594 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,5 +86,5 @@ title = "tad" [tool.pytest.ini_options] testpaths = [ - "tests" + "tad/tests" ] diff --git a/tad/alembic/env.py b/tad/alembic/env.py index cb802bc9..29f967bb 100644 --- a/tad/alembic/env.py +++ b/tad/alembic/env.py @@ -22,11 +22,11 @@ def get_url(): file = os.getenv("SQLITE_FILE", "./database") return f"{scheme}://{file}" - user = os.getenv("POSTGRES_USER", "postgres") - password = os.getenv("POSTGRES_PASSWORD", "") - server = os.getenv("POSTGRES_SERVER", "db") - port = os.getenv("POSTGRES_PORT", "5432") - db = os.getenv("POSTGRES_DB", "tad") + user = os.getenv("APP_DATABASE_USER", "postgres") + password = os.getenv("APP_DATABASE_PASSWORD", "") + server = os.getenv("APP_DATABASE_SERVER", "db") + port = os.getenv("APP_DATABASE_PORT", "5432") + db = os.getenv("APP_DATABASE_DB", "tad") return f"{scheme}://{user}:{password}@{server}:{port}/{db}" diff --git a/tad/core/config.py b/tad/core/config.py index f5c0f608..dc562c7d 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -40,16 +40,13 @@ def server_host(self) -> str: PROJECT_NAME: str = "TAD" - if ENVIRONMENT == "local": - SQLALCHEMY_SCHEME: str = "sqlite" - else: - SQLALCHEMY_SCHEME: str = "postgresql+psycopg" + SQLALCHEMY_SCHEME: str = "sqlite" - POSTGRES_SERVER: str = "db" - POSTGRES_PORT: int = 5432 - POSTGRES_USER: str = "postgres" - POSTGRES_PASSWORD: str - POSTGRES_DB: str = "tad" + APP_DATABASE_SERVER: str = "db" + APP_DATABASE_PORT: int = 5432 + APP_DATABASE_USER: str = "tad" + APP_DATABASE_PASSWORD: str + APP_DATABASE_DB: str = "tad" SQLITE_FILE: str = "./database" @@ -62,11 +59,11 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: return str( MultiHostUrl.build( scheme=self.SQLALCHEMY_SCHEME, - username=self.POSTGRES_USER, - password=self.POSTGRES_PASSWORD, - host=self.POSTGRES_SERVER, - port=self.POSTGRES_PORT, - path=self.POSTGRES_DB, + username=self.APP_DATABASE_USER, + password=self.APP_DATABASE_PASSWORD, + host=self.APP_DATABASE_SERVER, + port=self.APP_DATABASE_PORT, + path=self.APP_DATABASE_DB, ) ) @@ -83,7 +80,7 @@ def _check_default_secret(self, var_name: str, value: str | None) -> None: @model_validator(mode="after") def _enforce_non_default_secrets(self) -> Self: self._check_default_secret("SECRET_KEY", self.SECRET_KEY) - self._check_default_secret("POSTGRES_PASSWORD", self.POSTGRES_PASSWORD) + self._check_default_secret("APP_DATABASE_PASSWORD", self.APP_DATABASE_PASSWORD) return self From 835c365e57ea88ac3e591983472073ae1c270a22 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 7 May 2024 07:09:43 +0000 Subject: [PATCH 06/55] Update dependencies --- .pre-commit-config.yaml | 4 +- BUILD.md | 14 +- poetry.lock | 585 ++++++++++++++++++++-------------------- pyproject.toml | 6 +- 4 files changed, 308 insertions(+), 301 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0f092f19..92ba0826 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_language_version: python: python3.11 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: end-of-file-fixer - id: trailing-whitespace @@ -14,7 +14,7 @@ repos: - id: check-toml - id: detect-private-key - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.3 + rev: v0.4.3 hooks: - id: ruff - id: ruff-format diff --git a/BUILD.md b/BUILD.md index c8caae78..0c3fb396 100644 --- a/BUILD.md +++ b/BUILD.md @@ -50,4 +50,16 @@ For testing, linting and other feature we use several tools. You can look up the ## Devcontainers -[VSCode](https://code.visualstudio.com/) has great support for devcontainers. If your editor has support for devcontainers you can also use them to start the devcontainer. Devcontaines offer a great standardized environments for development. +[VSCode](https://code.visualstudio.com/) has great support for devcontainers. If your editor has support for devcontainers you can also use them to start the devcontainer. Devcontaines offer great standardized environments for development. + +## Updating dependencies + +Use poetry to update all python project dependencies +```shell +poetry update +``` + +Use pre-commit to update all hooks +```shell +pre-commit autoupdate +``` diff --git a/poetry.lock b/poetry.lock index fae51db5..ed569d77 100644 --- a/poetry.lock +++ b/poetry.lock @@ -161,63 +161,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.0" +version = "7.5.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, - {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, - {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, - {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, - {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, - {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, - {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, - {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, - {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, - {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, - {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, - {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, - {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, - {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, + {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, + {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, + {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, + {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, + {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, + {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, + {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, + {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, + {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, + {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, + {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, + {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, + {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, + {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, ] [package.extras] @@ -244,13 +244,13 @@ tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} [[package]] name = "cyclonedx-python-lib" -version = "7.3.2" +version = "7.3.4" description = "Python library for CycloneDX" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "cyclonedx_python_lib-7.3.2-py3-none-any.whl", hash = "sha256:fcbcbe4c99f87910a239de84f2bc0db4a429c208b4ef0436c0cbc3677ef1b790"}, - {file = "cyclonedx_python_lib-7.3.2.tar.gz", hash = "sha256:e2b12702e98da7bce89c28496c73964c2a0b128276078c1d0b398a8ba359e000"}, + {file = "cyclonedx_python_lib-7.3.4-py3-none-any.whl", hash = "sha256:8b6dc39f2281feb7fbf9b174fa5d8d3f8f7b51fd6f1d83e9b4c9bbd60ec2ab91"}, + {file = "cyclonedx_python_lib-7.3.4.tar.gz", hash = "sha256:f374855bd6b736b3a6be4eec93b5ca7f160c8282fe4ba5486518da11dbe83f1b"}, ] [package.dependencies] @@ -369,13 +369,13 @@ all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" [[package]] name = "filelock" -version = "3.13.4" +version = "3.14.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, - {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] @@ -632,13 +632,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -660,13 +660,13 @@ files = [ [[package]] name = "jsonschema" -version = "4.21.1" +version = "4.22.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, - {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, + {file = "jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802"}, + {file = "jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7"}, ] [package.dependencies] @@ -993,62 +993,57 @@ setuptools = "*" [[package]] name = "orjson" -version = "3.10.1" +version = "3.10.3" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.1-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8ec2fc456d53ea4a47768f622bb709be68acd455b0c6be57e91462259741c4f3"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e900863691d327758be14e2a491931605bd0aded3a21beb6ce133889830b659"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab6ecbd6fe57785ebc86ee49e183f37d45f91b46fc601380c67c5c5e9c0014a2"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af7c68b01b876335cccfb4eee0beef2b5b6eae1945d46a09a7c24c9faac7a77"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:915abfb2e528677b488a06eba173e9d7706a20fdfe9cdb15890b74ef9791b85e"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3fd4a36eff9c63d25503b439531d21828da9def0059c4f472e3845a081aa0b"}, - {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d229564e72cfc062e6481a91977a5165c5a0fdce11ddc19ced8471847a67c517"}, - {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9e00495b18304173ac843b5c5fbea7b6f7968564d0d49bef06bfaeca4b656f4e"}, - {file = "orjson-3.10.1-cp310-none-win32.whl", hash = "sha256:fd78ec55179545c108174ba19c1795ced548d6cac4d80d014163033c047ca4ea"}, - {file = "orjson-3.10.1-cp310-none-win_amd64.whl", hash = "sha256:50ca42b40d5a442a9e22eece8cf42ba3d7cd4cd0f2f20184b4d7682894f05eec"}, - {file = "orjson-3.10.1-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b345a3d6953628df2f42502297f6c1e1b475cfbf6268013c94c5ac80e8abc04c"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caa7395ef51af4190d2c70a364e2f42138e0e5fcb4bc08bc9b76997659b27dab"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b01d701decd75ae092e5f36f7b88a1e7a1d3bb7c9b9d7694de850fb155578d5a"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5028981ba393f443d8fed9049211b979cadc9d0afecf162832f5a5b152c6297"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31ff6a222ea362b87bf21ff619598a4dc1106aaafaea32b1c4876d692891ec27"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e852a83d7803d3406135fb7a57cf0c1e4a3e73bac80ec621bd32f01c653849c5"}, - {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2567bc928ed3c3fcd90998009e8835de7c7dc59aabcf764b8374d36044864f3b"}, - {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4ce98cac60b7bb56457bdd2ed7f0d5d7f242d291fdc0ca566c83fa721b52e92d"}, - {file = "orjson-3.10.1-cp311-none-win32.whl", hash = "sha256:813905e111318acb356bb8029014c77b4c647f8b03f314e7b475bd9ce6d1a8ce"}, - {file = "orjson-3.10.1-cp311-none-win_amd64.whl", hash = "sha256:03a3ca0b3ed52bed1a869163a4284e8a7b0be6a0359d521e467cdef7e8e8a3ee"}, - {file = "orjson-3.10.1-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f02c06cee680b1b3a8727ec26c36f4b3c0c9e2b26339d64471034d16f74f4ef5"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1aa2f127ac546e123283e437cc90b5ecce754a22306c7700b11035dad4ccf85"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2cf29b4b74f585225196944dffdebd549ad2af6da9e80db7115984103fb18a96"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1b130c20b116f413caf6059c651ad32215c28500dce9cd029a334a2d84aa66f"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d31f9a709e6114492136e87c7c6da5e21dfedebefa03af85f3ad72656c493ae9"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d1d169461726f271ab31633cf0e7e7353417e16fb69256a4f8ecb3246a78d6e"}, - {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57c294d73825c6b7f30d11c9e5900cfec9a814893af7f14efbe06b8d0f25fba9"}, - {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7f11dbacfa9265ec76b4019efffabaabba7a7ebf14078f6b4df9b51c3c9a8ea"}, - {file = "orjson-3.10.1-cp312-none-win32.whl", hash = "sha256:d89e5ed68593226c31c76ab4de3e0d35c760bfd3fbf0a74c4b2be1383a1bf123"}, - {file = "orjson-3.10.1-cp312-none-win_amd64.whl", hash = "sha256:aa76c4fe147fd162107ce1692c39f7189180cfd3a27cfbc2ab5643422812da8e"}, - {file = "orjson-3.10.1-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a2c6a85c92d0e494c1ae117befc93cf8e7bca2075f7fe52e32698da650b2c6d1"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9813f43da955197d36a7365eb99bed42b83680801729ab2487fef305b9ced866"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec917b768e2b34b7084cb6c68941f6de5812cc26c6f1a9fecb728e36a3deb9e8"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5252146b3172d75c8a6d27ebca59c9ee066ffc5a277050ccec24821e68742fdf"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:536429bb02791a199d976118b95014ad66f74c58b7644d21061c54ad284e00f4"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dfed3c3e9b9199fb9c3355b9c7e4649b65f639e50ddf50efdf86b45c6de04b5"}, - {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2b230ec35f188f003f5b543644ae486b2998f6afa74ee3a98fc8ed2e45960afc"}, - {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:01234249ba19c6ab1eb0b8be89f13ea21218b2d72d496ef085cfd37e1bae9dd8"}, - {file = "orjson-3.10.1-cp38-none-win32.whl", hash = "sha256:8a884fbf81a3cc22d264ba780920d4885442144e6acaa1411921260416ac9a54"}, - {file = "orjson-3.10.1-cp38-none-win_amd64.whl", hash = "sha256:dab5f802d52b182163f307d2b1f727d30b1762e1923c64c9c56dd853f9671a49"}, - {file = "orjson-3.10.1-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a51fd55d4486bc5293b7a400f9acd55a2dc3b5fc8420d5ffe9b1d6bb1a056a5e"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53521542a6db1411b3bfa1b24ddce18605a3abdc95a28a67b33f9145f26aa8f2"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:27d610df96ac18ace4931411d489637d20ab3b8f63562b0531bba16011998db0"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79244b1456e5846d44e9846534bd9e3206712936d026ea8e6a55a7374d2c0694"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d751efaa8a49ae15cbebdda747a62a9ae521126e396fda8143858419f3b03610"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27ff69c620a4fff33267df70cfd21e0097c2a14216e72943bd5414943e376d77"}, - {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ebc58693464146506fde0c4eb1216ff6d4e40213e61f7d40e2f0dde9b2f21650"}, - {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5be608c3972ed902e0143a5b8776d81ac1059436915d42defe5c6ae97b3137a4"}, - {file = "orjson-3.10.1-cp39-none-win32.whl", hash = "sha256:4ae10753e7511d359405aadcbf96556c86e9dbf3a948d26c2c9f9a150c52b091"}, - {file = "orjson-3.10.1-cp39-none-win_amd64.whl", hash = "sha256:fb5bc4caa2c192077fdb02dce4e5ef8639e7f20bec4e3a834346693907362932"}, - {file = "orjson-3.10.1.tar.gz", hash = "sha256:a883b28d73370df23ed995c466b4f6c708c1f7a9bdc400fe89165c96c7603204"}, + {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"}, + {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"}, + {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"}, + {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"}, + {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"}, + {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"}, + {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"}, + {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"}, + {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"}, + {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"}, + {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"}, + {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"}, + {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"}, + {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"}, + {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"}, + {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"}, ] [[package]] @@ -1380,13 +1375,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyright" -version = "1.1.360" +version = "1.1.361" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.360-py3-none-any.whl", hash = "sha256:7637f75451ac968b7cf1f8c51cfefb6d60ac7d086eb845364bc8ac03a026efd7"}, - {file = "pyright-1.1.360.tar.gz", hash = "sha256:784ddcda9745e9f5610483d7b963e9aa8d4f50d7755a9dffb28ccbeb27adce32"}, + {file = "pyright-1.1.361-py3-none-any.whl", hash = "sha256:c50fc94ce92b5c958cfccbbe34142e7411d474da43d6c14a958667e35b9df7ea"}, + {file = "pyright-1.1.361.tar.gz", hash = "sha256:1d67933315666b05d230c85ea8fb97aaa2056e4092a13df87b7765bb9e8f1a8d"}, ] [package.dependencies] @@ -1522,13 +1517,13 @@ files = [ [[package]] name = "referencing" -version = "0.35.0" +version = "0.35.1" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.35.0-py3-none-any.whl", hash = "sha256:8080727b30e364e5783152903672df9b6b091c926a146a759080b62ca3126cd6"}, - {file = "referencing-0.35.0.tar.gz", hash = "sha256:191e936b0c696d0af17ad7430a3dc68e88bc11be6514f4757dc890f04ab05889"}, + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, ] [package.dependencies] @@ -1562,136 +1557,136 @@ files = [ [[package]] name = "rpds-py" -version = "0.18.0" +version = "0.18.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, - {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, - {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, - {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, - {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, - {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, - {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, - {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, - {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, - {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, - {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, - {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, - {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, + {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, + {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, + {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, + {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, + {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, + {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, + {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, + {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, + {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, + {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, + {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, ] [[package]] name = "ruff" -version = "0.4.2" +version = "0.4.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d14dc8953f8af7e003a485ef560bbefa5f8cc1ad994eebb5b12136049bbccc5"}, - {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:24016ed18db3dc9786af103ff49c03bdf408ea253f3cb9e3638f39ac9cf2d483"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2e06459042ac841ed510196c350ba35a9b24a643e23db60d79b2db92af0c2b"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3afabaf7ba8e9c485a14ad8f4122feff6b2b93cc53cd4dad2fd24ae35112d5c5"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799eb468ea6bc54b95527143a4ceaf970d5aa3613050c6cff54c85fda3fde480"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ec4ba9436a51527fb6931a8839af4c36a5481f8c19e8f5e42c2f7ad3a49f5069"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a2243f8f434e487c2a010c7252150b1fdf019035130f41b77626f5655c9ca22"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8772130a063f3eebdf7095da00c0b9898bd1774c43b336272c3e98667d4fb8fa"}, - {file = "ruff-0.4.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab165ef5d72392b4ebb85a8b0fbd321f69832a632e07a74794c0e598e7a8376"}, - {file = "ruff-0.4.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f32cadf44c2020e75e0c56c3408ed1d32c024766bd41aedef92aa3ca28eef68"}, - {file = "ruff-0.4.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:22e306bf15e09af45ca812bc42fa59b628646fa7c26072555f278994890bc7ac"}, - {file = "ruff-0.4.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82986bb77ad83a1719c90b9528a9dd663c9206f7c0ab69282af8223566a0c34e"}, - {file = "ruff-0.4.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:652e4ba553e421a6dc2a6d4868bc3b3881311702633eb3672f9f244ded8908cd"}, - {file = "ruff-0.4.2-py3-none-win32.whl", hash = "sha256:7891ee376770ac094da3ad40c116258a381b86c7352552788377c6eb16d784fe"}, - {file = "ruff-0.4.2-py3-none-win_amd64.whl", hash = "sha256:5ec481661fb2fd88a5d6cf1f83403d388ec90f9daaa36e40e2c003de66751798"}, - {file = "ruff-0.4.2-py3-none-win_arm64.whl", hash = "sha256:cbd1e87c71bca14792948c4ccb51ee61c3296e164019d2d484f3eaa2d360dfaf"}, - {file = "ruff-0.4.2.tar.gz", hash = "sha256:33bcc160aee2520664bc0859cfeaebc84bb7323becff3f303b8f1f2d81cb4edc"}, + {file = "ruff-0.4.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b70800c290f14ae6fcbb41bbe201cf62dfca024d124a1f373e76371a007454ce"}, + {file = "ruff-0.4.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:08a0d6a22918ab2552ace96adeaca308833873a4d7d1d587bb1d37bae8728eb3"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba1f14df3c758dd7de5b55fbae7e1c8af238597961e5fb628f3de446c3c40c5"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:819fb06d535cc76dfddbfe8d3068ff602ddeb40e3eacbc90e0d1272bb8d97113"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bfc9e955e6dc6359eb6f82ea150c4f4e82b660e5b58d9a20a0e42ec3bb6342b"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:510a67d232d2ebe983fddea324dbf9d69b71c4d2dfeb8a862f4a127536dd4cfb"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9ff11cd9a092ee7680a56d21f302bdda14327772cd870d806610a3503d001f"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29efff25bf9ee685c2c8390563a5b5c006a3fee5230d28ea39f4f75f9d0b6f2f"}, + {file = "ruff-0.4.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b00e0bcccf0fc8d7186ed21e311dffd19761cb632241a6e4fe4477cc80ef6e"}, + {file = "ruff-0.4.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:262f5635e2c74d80b7507fbc2fac28fe0d4fef26373bbc62039526f7722bca1b"}, + {file = "ruff-0.4.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7363691198719c26459e08cc17c6a3dac6f592e9ea3d2fa772f4e561b5fe82a3"}, + {file = "ruff-0.4.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eeb039f8428fcb6725bb63cbae92ad67b0559e68b5d80f840f11914afd8ddf7f"}, + {file = "ruff-0.4.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:927b11c1e4d0727ce1a729eace61cee88a334623ec424c0b1c8fe3e5f9d3c865"}, + {file = "ruff-0.4.3-py3-none-win32.whl", hash = "sha256:25cacda2155778beb0d064e0ec5a3944dcca9c12715f7c4634fd9d93ac33fd30"}, + {file = "ruff-0.4.3-py3-none-win_amd64.whl", hash = "sha256:7a1c3a450bc6539ef00da6c819fb1b76b6b065dec585f91456e7c0d6a0bbc725"}, + {file = "ruff-0.4.3-py3-none-win_arm64.whl", hash = "sha256:71ca5f8ccf1121b95a59649482470c5601c60a416bf189d553955b0338e34614"}, + {file = "ruff-0.4.3.tar.gz", hash = "sha256:ff0a3ef2e3c4b6d133fbedcf9586abfbe38d076041f2dc18ffb2c7e0485d5a07"}, ] [[package]] @@ -1745,60 +1740,60 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.29" +version = "2.0.30" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c142852ae192e9fe5aad5c350ea6befe9db14370b34047e1f0f7cf99e63c63b"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a1e69d4e26f71e750e9ad6fdc8614fbddb67cfe2173a3628a2566034e223c7"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef3fbccb4058355053c51b82fd3501a6e13dd808c8d8cd2561e610c5456013c"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6753305936eddc8ed190e006b7bb33a8f50b9854823485eed3a886857ab8d1"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0f3ca96af060a5250a8ad5a63699180bc780c2edf8abf96c58af175921df847a"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4520047006b1d3f0d89e0532978c0688219857eb2fee7c48052560ae76aca1e"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-win32.whl", hash = "sha256:b2a0e3cf0caac2085ff172c3faacd1e00c376e6884b5bc4dd5b6b84623e29e4f"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-win_amd64.whl", hash = "sha256:01d10638a37460616708062a40c7b55f73e4d35eaa146781c683e0fa7f6c43fb"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:308ef9cb41d099099fffc9d35781638986870b29f744382904bf9c7dadd08513"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:296195df68326a48385e7a96e877bc19aa210e485fa381c5246bc0234c36c78e"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a13b917b4ffe5a0a31b83d051d60477819ddf18276852ea68037a144a506efb9"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f6d971255d9ddbd3189e2e79d743ff4845c07f0633adfd1de3f63d930dbe673"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61405ea2d563407d316c63a7b5271ae5d274a2a9fbcd01b0aa5503635699fa1e"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de7202ffe4d4a8c1e3cde1c03e01c1a3772c92858837e8f3879b497158e4cb44"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-win32.whl", hash = "sha256:b5d7ed79df55a731749ce65ec20d666d82b185fa4898430b17cb90c892741520"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-win_amd64.whl", hash = "sha256:205f5a2b39d7c380cbc3b5dcc8f2762fb5bcb716838e2d26ccbc54330775b003"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d96710d834a6fb31e21381c6d7b76ec729bd08c75a25a5184b1089141356171f"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52de4736404e53c5c6a91ef2698c01e52333988ebdc218f14c833237a0804f1b"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c7b02525ede2a164c5fa5014915ba3591730f2cc831f5be9ff3b7fd3e30958e"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dfefdb3e54cd15f5d56fd5ae32f1da2d95d78319c1f6dfb9bcd0eb15d603d5d"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a88913000da9205b13f6f195f0813b6ffd8a0c0c2bd58d499e00a30eb508870c"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fecd5089c4be1bcc37c35e9aa678938d2888845a134dd016de457b942cf5a758"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-win32.whl", hash = "sha256:8197d6f7a3d2b468861ebb4c9f998b9df9e358d6e1cf9c2a01061cb9b6cf4e41"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-win_amd64.whl", hash = "sha256:9b19836ccca0d321e237560e475fd99c3d8655d03da80c845c4da20dda31b6e1"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87a1d53a5382cdbbf4b7619f107cc862c1b0a4feb29000922db72e5a66a5ffc0"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0732dffe32333211801b28339d2a0babc1971bc90a983e3035e7b0d6f06b93"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90453597a753322d6aa770c5935887ab1fc49cc4c4fdd436901308383d698b4b"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ea311d4ee9a8fa67f139c088ae9f905fcf0277d6cd75c310a21a88bf85e130f5"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5f20cb0a63a3e0ec4e169aa8890e32b949c8145983afa13a708bc4b0a1f30e03"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-win32.whl", hash = "sha256:e5bbe55e8552019c6463709b39634a5fc55e080d0827e2a3a11e18eb73f5cdbd"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-win_amd64.whl", hash = "sha256:c2f9c762a2735600654c654bf48dad388b888f8ce387b095806480e6e4ff6907"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e614d7a25a43a9f54fcce4675c12761b248547f3d41b195e8010ca7297c369c"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:471fcb39c6adf37f820350c28aac4a7df9d3940c6548b624a642852e727ea586"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:988569c8732f54ad3234cf9c561364221a9e943b78dc7a4aaf35ccc2265f1930"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dddaae9b81c88083e6437de95c41e86823d150f4ee94bf24e158a4526cbead01"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:334184d1ab8f4c87f9652b048af3f7abea1c809dfe526fb0435348a6fef3d380"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:38b624e5cf02a69b113c8047cf7f66b5dfe4a2ca07ff8b8716da4f1b3ae81567"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-win32.whl", hash = "sha256:bab41acf151cd68bc2b466deae5deeb9e8ae9c50ad113444151ad965d5bf685b"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-win_amd64.whl", hash = "sha256:52c8011088305476691b8750c60e03b87910a123cfd9ad48576d6414b6ec2a1d"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3071ad498896907a5ef756206b9dc750f8e57352113c19272bdfdc429c7bd7de"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dba622396a3170974f81bad49aacebd243455ec3cc70615aeaef9e9613b5bca5"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b184e3de58009cc0bf32e20f137f1ec75a32470f5fede06c58f6c355ed42a72"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c37f1050feb91f3d6c32f864d8e114ff5545a4a7afe56778d76a9aec62638ba"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bda7ce59b06d0f09afe22c56714c65c957b1068dee3d5e74d743edec7daba552"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25664e18bef6dc45015b08f99c63952a53a0a61f61f2e48a9e70cec27e55f699"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-win32.whl", hash = "sha256:77d29cb6c34b14af8a484e831ab530c0f7188f8efed1c6a833a2c674bf3c26ec"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-win_amd64.whl", hash = "sha256:04c487305ab035a9548f573763915189fc0fe0824d9ba28433196f8436f1449c"}, - {file = "SQLAlchemy-2.0.29-py3-none-any.whl", hash = "sha256:dc4ee2d4ee43251905f88637d5281a8d52e916a021384ec10758826f5cbae305"}, - {file = "SQLAlchemy-2.0.29.tar.gz", hash = "sha256:bd9566b8e58cabd700bc367b60e90d9349cd16f0984973f98a9a09f9c64e86f0"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"}, + {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, + {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, ] [package.dependencies] @@ -2066,13 +2061,13 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.26.0" +version = "20.26.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.0-py3-none-any.whl", hash = "sha256:0846377ea76e818daaa3e00a4365c018bc3ac9760cbb3544de542885aad61fb3"}, - {file = "virtualenv-20.26.0.tar.gz", hash = "sha256:ec25a9671a5102c8d2657f62792a27b48f016664c6873f6beed3800008577210"}, + {file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"}, + {file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"}, ] [package.dependencies] @@ -2281,4 +2276,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "49f1eb7773057c7c8e5ae1439589ac95e077fa39389131a9e691e16d95cd9848" +content-hash = "9adec4b3c1e9ffade2ba2b7694e6d9cf56e42752e6ff426fcba43078256f6c4d" diff --git a/pyproject.toml b/pyproject.toml index 1bfd1594..c3ff31f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,12 +29,12 @@ alembic = "^1.13.1" [tool.poetry.group.test.dependencies] pytest = "^8.1.1" -coverage = "^7.5.0" +coverage = "^7.5.1" [tool.poetry.group.dev.dependencies] -ruff = "^0.4.2" +ruff = "^0.4.3" pre-commit = "^3.7.0" -pyright = "^1.1.360" +pyright = "^1.1.361" cyclonedx-bom = "^4.4.2" pip-licenses = "^4.4.0" From c94b3e51190c66f125bdd19a879c97eaa8310918 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 12:58:40 +0000 Subject: [PATCH 07/55] Add alembic test version --- .pre-commit-config.yaml | 1 - tad/alembic/env.py | 2 ++ tad/alembic/versions/99fb931b1324_.py | 24 +++--------------------- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 92ba0826..af8a30ea 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,6 @@ repos: - id: trailing-whitespace - id: check-yaml - id: check-json - - id: check-toml - id: check-added-large-files - id: check-merge-conflict - id: check-toml diff --git a/tad/alembic/env.py b/tad/alembic/env.py index 29f967bb..5e3667ff 100644 --- a/tad/alembic/env.py +++ b/tad/alembic/env.py @@ -57,6 +57,8 @@ def run_migrations_online(): """ configuration = config.get_section(config.config_ini_section) + if configuration is None: + raise Exception("Failed to get configuration section") # noqa: TRY003, TRY002 configuration["sqlalchemy.url"] = get_url() connectable = engine_from_config( configuration, diff --git a/tad/alembic/versions/99fb931b1324_.py b/tad/alembic/versions/99fb931b1324_.py index 926c4e74..16719e38 100644 --- a/tad/alembic/versions/99fb931b1324_.py +++ b/tad/alembic/versions/99fb931b1324_.py @@ -9,7 +9,6 @@ from collections.abc import Sequence import sqlalchemy as sa -import sqlmodel.sql.sqltypes from alembic import op @@ -21,32 +20,15 @@ def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### op.create_table( "hero", sa.Column("id", sa.Integer(), nullable=False), - sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("secret_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.Column("secret_name", sa.String(), nullable=False), sa.Column("age", sa.Integer(), nullable=True), sa.PrimaryKeyConstraint("id"), ) - op.create_table( - "user", - sa.Column("email", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("is_active", sa.Boolean(), nullable=False), - sa.Column("is_superuser", sa.Boolean(), nullable=False), - sa.Column("full_name", sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("hashed_password", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True) - # ### end Alembic commands ### def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f("ix_user_email"), table_name="user") - op.drop_table("user") - op.drop_table("hero") - # ### end Alembic commands ### + pass From b7d42d5e2457ad27c230c339d47ba0afa90d4746 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:00:04 +0000 Subject: [PATCH 08/55] Add devcontainer vscode settings for python --- .devcontainer/devcontainer.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 596a38db..da0d1e50 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -16,12 +16,14 @@ "vscode": { "extensions": [ "ms-python.python", + "ms-python.vscode-pylance", "editorconfig.editorconfig", "streetsidesoftware.code-spell-checker", "redhat.vscode-yaml", "charliermarsh.ruff", "github.vscode-github-actions", - "mhutchie.git-graph" + "mhutchie.git-graph", + "markis.code-coverage" ], "settings": { "editor.formatOnPaste": false, @@ -39,7 +41,10 @@ "terminal.integrated.defaultProfile.linux": "zsh", "[python]": { "editor.defaultFormatter": "charliermarsh.ruff" - } + }, + "python.languageServer": "Pylance", + "python.analysis.typeCheckingMode": "strict", + "markiscodecoverage.searchCriteria": "coverage.lcov" } } } From 3ca72c26144fb9a17c7c93b0e98e96b6e04f204b Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:01:10 +0000 Subject: [PATCH 09/55] Update vscode settings to improve integration with project --- .vscode/extensions.json | 3 +++ .vscode/launch.json | 8 ++++---- .vscode/tasks.json | 18 +++++++++++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4b7ea0f2..15f9cc4d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,10 +1,13 @@ { "recommendations": [ "ms-python.python", + "ms-python.vscode-pylance", "editorconfig.editorconfig", "streetsidesoftware.code-spell-checker", "redhat.vscode-yaml", "charliermarsh.ruff", "github.vscode-github-actions", + "mhutchie.git-graph", + "markis.code-coverage" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index caded2c2..debd23c4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,9 +5,9 @@ "name": "tad - fastapi", "type": "debugpy", "request": "launch", - "module": "uvicorn", - "justMyCode": false, - "args": ["main:app"], + "module": "granian", + "justMyCode": true, + "args": ["--interface","asgi","main:app", "--reload"], "cwd": "${workspaceFolder}/tad/", "env": { "PYTHONPATH": "${workspaceFolder}" @@ -19,7 +19,7 @@ "request": "launch", "module": "pytest", "cwd": "${workspaceFolder}", - "justMyCode": false, + "justMyCode": true, "args": [] } ] diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 087db6b7..43e93d22 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -14,10 +14,22 @@ "problemMatcher": [] }, { - "label": "Ruff", - "detail": "format and lint code.", + "label": "Ruff lint", + "detail": "lint code.", "type": "shell", - "command": "poetry run ruff format && poetry run ruff check", + "command": "poetry run ruff check --fix", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Ruff format", + "detail": "format code.", + "type": "shell", + "command": "poetry run ruff format", "group": "test", "presentation": { "reveal": "always", From c95f572d02aaa38ccb027ff4479794ea9be69fcb Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:02:09 +0000 Subject: [PATCH 10/55] Update project dependencies and filter warnings for pytest --- poetry.lock | 601 ++++++++++++++++--------------------------------- pyproject.toml | 15 +- 2 files changed, 206 insertions(+), 410 deletions(-) diff --git a/poetry.lock b/poetry.lock index ed569d77..6b6eb6e3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -288,41 +288,6 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] -[[package]] -name = "dnspython" -version = "2.6.1" -description = "DNS toolkit" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, - {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, -] - -[package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=41)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=0.9.25)"] -idna = ["idna (>=3.6)"] -trio = ["trio (>=0.23)"] -wmi = ["wmi (>=1.5.1)"] - -[[package]] -name = "email-validator" -version = "2.1.1" -description = "A robust email address syntax and deliverability validation library." -optional = false -python-versions = ">=3.8" -files = [ - {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, - {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, -] - -[package.dependencies] -dnspython = ">=2.0.0" -idna = ">=2.0.0" - [[package]] name = "exceptiongroup" version = "1.2.1" @@ -349,20 +314,9 @@ files = [ ] [package.dependencies] -email_validator = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} -httpx = {version = ">=0.23.0", optional = true, markers = "extra == \"all\""} -itsdangerous = {version = ">=1.1.0", optional = true, markers = "extra == \"all\""} -jinja2 = {version = ">=2.11.2", optional = true, markers = "extra == \"all\""} -orjson = {version = ">=3.2.1", optional = true, markers = "extra == \"all\""} pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -pydantic-extra-types = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} -pydantic-settings = {version = ">=2.0.0", optional = true, markers = "extra == \"all\""} -python-multipart = {version = ">=0.0.7", optional = true, markers = "extra == \"all\""} -pyyaml = {version = ">=5.3.1", optional = true, markers = "extra == \"all\""} starlette = ">=0.37.2,<0.38.0" typing-extensions = ">=4.8.0" -ujson = {version = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0", optional = true, markers = "extra == \"all\""} -uvicorn = {version = ">=0.12.0", extras = ["standard"], optional = true, markers = "extra == \"all\""} [package.extras] all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] @@ -394,6 +348,85 @@ files = [ {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, ] +[[package]] +name = "granian" +version = "1.3.1" +description = "A Rust HTTP server for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "granian-1.3.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a6c459af46ff5b3d7d2fcef6d4cae7e1d962c05d9041349efeb49c6e03a3a4ad"}, + {file = "granian-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6eb338e9d78f14ce9fcee8afd39691d703d26f6a17f0a31e807e790f62818cc5"}, + {file = "granian-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7885ffa80977b9ab96308b11311cf01387c880b0ee7b39c4051f18b34706389a"}, + {file = "granian-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df1fa92c4789d17183810929658cf2f7969b64e04d3777c843196d73a68ce6d"}, + {file = "granian-1.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:396203f11ce8406710055194ff75a54bc17703f5139cdddc86f2e9cbb80ebba2"}, + {file = "granian-1.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b783057f22408b4515ddbfbd9fbd8508a9df29e7523c774c3bc3da14cc56c27e"}, + {file = "granian-1.3.1-cp310-none-win_amd64.whl", hash = "sha256:6dd3b46a457935d01cc80e1294c22f00fd0d700c91df498030557bcd1a99c9b9"}, + {file = "granian-1.3.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f682b40f39286423f1ae5426ad3cbafe0b36e6521bb360cc0882385b4f11cd28"}, + {file = "granian-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:310581f719ea7c43a9bab1c71ab9c62b09b3b91bd254fed0af8b707531d27a8f"}, + {file = "granian-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bc8e9d2d13d251f442b2241cc73d53025a5a4b17a563e957286562df0bafca"}, + {file = "granian-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f472b93a0ed0e4a1675de465eedf96f67b9585e3ae03272bc250734ff7c5e0"}, + {file = "granian-1.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:422077b6f95a0c4cc6be139c38230595de1ee3e64f5cd4a5b366ac9e94a70e1b"}, + {file = "granian-1.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2767b4c4a2c0fdb1f5c4f1b843901063afc65d8c2be118f063b08eb28b8ac36"}, + {file = "granian-1.3.1-cp311-none-win_amd64.whl", hash = "sha256:2c606bfdeab0709aadfff7b705c3e25fb94434dc5acc26b12531aec3baef9558"}, + {file = "granian-1.3.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e9f60164ba0588db3701ed4b2348893c118257e4374da1747813fb4c33870d68"}, + {file = "granian-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2993c8b9e3fe8e82ad60e892d7bb7ae77670038e06cff9e89f770c858937e697"}, + {file = "granian-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7245c041a0bca32e99457f398a9c8b8774609b56e548326aa906b465fae0ec6c"}, + {file = "granian-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3118bbb0f47c0374b968787c780f5edcde2653fd8874dd3c753b5bd6b71f6cdf"}, + {file = "granian-1.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:411eb1520496f03beca7d3ab28b6adb7738d6f0ac500f2f45dbd8ea24d9639b9"}, + {file = "granian-1.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65cd2cf834dd9fe393add340c4cf2237184684aac4e503b0b447fc691c704378"}, + {file = "granian-1.3.1-cp312-none-win_amd64.whl", hash = "sha256:54517442e7622de643444bd43a13e4b2461771e3c38313c267a9ac0e476a9abb"}, + {file = "granian-1.3.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3350c71e4fd36f2eb30144cad20f5e21f40b6494e6ce17fa2b6c26d3099c661a"}, + {file = "granian-1.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d4b2015e5937435016b2c25f545ac476ffcde6aa2526fa8252a2cec5c5686353"}, + {file = "granian-1.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1e07cabc89ba015fb717232181c14405852237ded401bb962728f07642e125"}, + {file = "granian-1.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50501a23c128c2f491d4454b2185f089070f6ec147f4742531c2b8460136a3b9"}, + {file = "granian-1.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5244bb8e8d404cba127b0737ad89ad2a624df00995ed471e7584f3f1b957c0df"}, + {file = "granian-1.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f7cde47ec2eecca3773806c1f63a3a71392e46be26711e1b6d988924d4acf6bf"}, + {file = "granian-1.3.1-cp38-none-win_amd64.whl", hash = "sha256:fd3d8106d96f85e50dc128d72b94a5234e6452e9976befb258af1278124fde4f"}, + {file = "granian-1.3.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3ededcc3cea77077f793903ca8263149afcbefb05a04e1e8fa68110f4059aabb"}, + {file = "granian-1.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b37605f3427605599a9ac1312825b386f1c41f1896f59bbbf0ce3055ac817354"}, + {file = "granian-1.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34cd9cc12401186a4d4e54c0fdae0471d670c3d3bc555e0912ac2c45917840e"}, + {file = "granian-1.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8a936228840b9915cd153fc9dc570055df81ee43f5eea6bc30e18e73e3c5fe4"}, + {file = "granian-1.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:10affc0400b257efd2509d1ce15f3eaef14b0b45fcd821b036de483b9bcae38a"}, + {file = "granian-1.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ae60fb4b5fd3f5489bab5f5c36b2a72b1195f5f990aac90cde5ec3de1588e5a"}, + {file = "granian-1.3.1-cp39-none-win_amd64.whl", hash = "sha256:e3132f37da4e38b85fe5ae6360e6b48ce340cb98349af148433288a6bbdf1d3e"}, + {file = "granian-1.3.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5d57123c49b72d282b94a8842ac274d161016f70b289b3c08e08f2f7d8283c6e"}, + {file = "granian-1.3.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1cbfeeb22d95bb6cfc797f996be60f21a643d603c6e4a0eb56ebb1a4015b5e25"}, + {file = "granian-1.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b332bf511130a68cc5b17f0b8927125be6769e8cdf46ebd297fdffc9fa5d66ff"}, + {file = "granian-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57019cdfbb4e5b3b1903c6b08d308732ed5341b6c441916f934cb240103729f1"}, + {file = "granian-1.3.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1406d803a5f3c9b11a6848fa1abdc5fc2f39f527675f61368c77cb843b8d3bfc"}, + {file = "granian-1.3.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:135a61579bf2fd20dc86ea6c754117ee661c884cf8bd8917f765b47e9e6773c8"}, + {file = "granian-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1aebcc9dc3556917ba5d2995165370fd3e04d533c54c1ba2ca611c2d94d036e0"}, + {file = "granian-1.3.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad6a874d56e249020ed45e1a840cbf21a3d414fde89fbe0153035ab37a16dc37"}, + {file = "granian-1.3.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:aa42e80b54af7fa32db57d4382115c0267830c3e17eac17811e9e613c763cf45"}, + {file = "granian-1.3.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2132f60309eea7bf9d6a847582853a067b0ca51c0fd4927d18e54a251a8707b5"}, + {file = "granian-1.3.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9592d3eaf6d0f14abfc825eccd55b62b74abd4513da8fabbab1eebd2d013b71"}, + {file = "granian-1.3.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c71b18d3a6f5980ab044861678cccce9e6fd6c5e401cc53b02f7605f2f31cb9d"}, + {file = "granian-1.3.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:09818af44ac163d8a2c2df068f4db2d3a6032d3d22a226a309fcaf4989e6798e"}, + {file = "granian-1.3.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0145fa076a6ad993498e255c67b4fd647a03803a51ee30de856ba66659dd0b1e"}, + {file = "granian-1.3.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1bc2879cc56fbdf7350481f40a213ceba13360a281b1748b0e2ed1edc3d7630d"}, + {file = "granian-1.3.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:67bac881cbcc2bb56a1a0a4190c97c73314088f8ddfdd99b9379959e432dc9d3"}, + {file = "granian-1.3.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48df73ae1840a223b85e93de870c7cc8314b918e8e5af4de9220a2510c6cc2f6"}, + {file = "granian-1.3.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:555a573754dd8b4c01a75a75d7dcbad016e8a726c06f8fc10b530a9ea58d0c50"}, + {file = "granian-1.3.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:9cb51fb2b37567a24f984dc6fcd2a3ba4a40c1af6b794e2f4a74206989eda11f"}, + {file = "granian-1.3.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:70676812437274aab6520e11f729e870da058c894406ac6048dfb631cd90523d"}, + {file = "granian-1.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:39a7720d5bc2867863c5a9844eff8cc4ccf2a3ce916390c8015a81c31f725703"}, + {file = "granian-1.3.1.tar.gz", hash = "sha256:4b5381a5e6eb20e702c72b8c87becdea36bc8f3e322c5fe7bfca53982c537e51"}, +] + +[package.dependencies] +click = ">=8.0.0" +uvloop = {version = ">=0.18.0,<0.19.0", markers = "sys_platform != \"win32\" and platform_python_implementation == \"CPython\""} +watchfiles = {version = ">=0.21,<1.0", optional = true, markers = "extra == \"reload\""} + +[package.extras] +all = ["granian[pname,reload]"] +dev = ["granian[all,lint,test]"] +lint = ["ruff (>=0.1.0,<0.2.0)"] +pname = ["setproctitle (>=1.3.3,<1.4.0)"] +reload = ["watchfiles (>=0.21,<1.0)"] +test = ["httpx (>=0.25.0,<0.26.0)", "pytest (>=7.4.2,<7.5.0)", "pytest-asyncio (>=0.21.1,<0.22.0)", "websockets (>=11.0,<12.0)"] + [[package]] name = "greenlet" version = "3.0.3" @@ -497,54 +530,6 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] trio = ["trio (>=0.22.0,<0.26.0)"] -[[package]] -name = "httptools" -version = "0.6.1" -description = "A collection of framework independent HTTP protocol utils." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, - {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, - {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, - {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, - {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, - {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, - {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, -] - -[package.extras] -test = ["Cython (>=0.29.24,<0.30.0)"] - [[package]] name = "httpx" version = "0.27.0" @@ -619,17 +604,6 @@ files = [ [package.dependencies] arrow = ">=0.15.0" -[[package]] -name = "itsdangerous" -version = "2.2.0" -description = "Safely pass data to untrusted environments and back." -optional = false -python-versions = ">=3.8" -files = [ - {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, - {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, -] - [[package]] name = "jinja2" version = "3.1.4" @@ -991,61 +965,6 @@ files = [ [package.dependencies] setuptools = "*" -[[package]] -name = "orjson" -version = "3.10.3" -description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -optional = false -python-versions = ">=3.8" -files = [ - {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"}, - {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"}, - {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"}, - {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"}, - {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"}, - {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"}, - {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"}, - {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"}, - {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"}, - {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"}, - {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"}, - {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"}, - {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"}, - {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"}, - {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"}, - {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"}, - {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"}, - {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"}, - {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"}, - {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"}, - {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"}, - {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"}, - {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"}, - {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"}, - {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"}, - {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"}, -] - [[package]] name = "packageurl-python" version = "0.15.0" @@ -1188,6 +1107,7 @@ files = [ ] [package.dependencies] +psycopg-binary = {version = "3.1.18", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} typing-extensions = ">=4.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} @@ -1199,6 +1119,80 @@ docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)" pool = ["psycopg-pool"] test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] +[[package]] +name = "psycopg-binary" +version = "3.1.18" +description = "PostgreSQL database adapter for Python -- C optimisation distribution" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c323103dfa663b88204cf5f028e83c77d7a715f9b6f51d2bbc8184b99ddd90a"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:887f8d856c91510148be942c7acd702ccf761a05f59f8abc123c22ab77b5a16c"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d322ba72cde4ca2eefc2196dad9ad7e52451acd2f04e3688d590290625d0c970"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:489aa4fe5a0b653b68341e9e44af247dedbbc655326854aa34c163ef1bcb3143"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ff0948457bfa8c0d35c46e3a75193906d1c275538877ba65907fd67aa059ad"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15e3653c82384b043d820fc637199b5c6a36b37fa4a4943e0652785bb2bad5d"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f8ff3bc08b43f36fdc24fedb86d42749298a458c4724fb588c4d76823ac39f54"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1729d0e3dfe2546d823841eb7a3d003144189d6f5e138ee63e5227f8b75276a5"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:13bcd3742112446037d15e360b27a03af4b5afcf767f5ee374ef8f5dd7571b31"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:320047e3d3554b857e16c2b6b615a85e0db6a02426f4d203a4594a2f125dfe57"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-win_amd64.whl", hash = "sha256:888a72c2aca4316ca6d4a619291b805677bae99bba2f6e31a3c18424a48c7e4d"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e4de16a637ec190cbee82e0c2dc4860fed17a23a35f7a1e6dc479a5c6876722"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6432047b8b24ef97e3fbee1d1593a0faaa9544c7a41a2c67d1f10e7621374c83"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d684227ef8212e27da5f2aff9d4d303cc30b27ac1702d4f6881935549486dd5"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67284e2e450dc7a9e4d76e78c0bd357dc946334a3d410defaeb2635607f632cd"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c9b6bd7fb5c6638cb32469674707649b526acfe786ba6d5a78ca4293d87bae4"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7121acc783c4e86d2d320a7fb803460fab158a7f0a04c5e8c5d49065118c1e73"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e28ff8f3de7b56588c2a398dc135fd9f157d12c612bd3daa7e6ba9872337f6f5"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c84a0174109f329eeda169004c7b7ca2e884a6305acab4a39600be67f915ed38"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:531381f6647fc267383dca88dbe8a70d0feff433a8e3d0c4939201fea7ae1b82"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b293e01057e63c3ac0002aa132a1071ce0fdb13b9ee2b6b45d3abdb3525c597d"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-win_amd64.whl", hash = "sha256:780a90bcb69bf27a8b08bc35b958e974cb6ea7a04cdec69e737f66378a344d68"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:87dd9154b757a5fbf6d590f6f6ea75f4ad7b764a813ae04b1d91a70713f414a1"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f876ebbf92db70125f6375f91ab4bc6b27648aa68f90d661b1fc5affb4c9731c"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d2f0cb45e4574f8b2fe7c6d0a0e2eb58903a4fd1fbaf60954fba82d595ab7"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd27f713f2e5ef3fd6796e66c1a5203a27a30ecb847be27a78e1df8a9a5ae68c"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c38a4796abf7380f83b1653c2711cb2449dd0b2e5aca1caa75447d6fa5179c69"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2f7f95746efd1be2dc240248cc157f4315db3fd09fef2adfcc2a76e24aa5741"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4085f56a8d4fc8b455e8f44380705c7795be5317419aa5f8214f315e4205d804"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2e2484ae835dedc80cdc7f1b1a939377dc967fed862262cfd097aa9f50cade46"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c2b039ae0c45eee4cd85300ef802c0f97d0afc78350946a5d0ec77dd2d7e834"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f54978c4b646dec77fefd8485fa82ec1a87807f334004372af1aaa6de9539a5"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-win_amd64.whl", hash = "sha256:9ffcbbd389e486d3fd83d30107bbf8b27845a295051ccabde240f235d04ed921"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c76659ae29a84f2c14f56aad305dd00eb685bd88f8c0a3281a9a4bc6bd7d2aa7"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7afcd6f1d55992f26d9ff7b0bd4ee6b475eb43aa3f054d67d32e09f18b0065"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:639dd78ac09b144b0119076783cb64e1128cc8612243e9701d1503c816750b2e"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1cf59e0bb12e031a48bb628aae32df3d0c98fd6c759cb89f464b1047f0ca9c8"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e262398e5d51563093edf30612cd1e20fedd932ad0994697d7781ca4880cdc3d"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:59701118c7d8842e451f1e562d08e8708b3f5d14974eefbce9374badd723c4ae"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dea4a59da7850192fdead9da888e6b96166e90608cf39e17b503f45826b16f84"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4575da95fc441244a0e2ebaf33a2b2f74164603341d2046b5cde0a9aa86aa7e2"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:812726266ab96de681f2c7dbd6b734d327f493a78357fcc16b2ac86ff4f4e080"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-win_amd64.whl", hash = "sha256:3e7ce4d988112ca6c75765c7f24c83bdc476a6a5ce00878df6c140ca32c3e16d"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:02bd4da45d5ee9941432e2e9bf36fa71a3ac21c6536fe7366d1bd3dd70d6b1e7"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:39242546383f6b97032de7af30edb483d237a0616f6050512eee7b218a2aa8ee"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d46ae44d66bf6058a812467f6ae84e4e157dee281bfb1cfaeca07dee07452e85"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad35ac7fd989184bf4d38a87decfb5a262b419e8ba8dcaeec97848817412c64a"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:247474af262bdd5559ee6e669926c4f23e9cf53dae2d34c4d991723c72196404"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ebecbf2406cd6875bdd2453e31067d1bd8efe96705a9489ef37e93b50dc6f09"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1859aeb2133f5ecdd9cbcee155f5e38699afc06a365f903b1512c765fd8d457e"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:da917f6df8c6b2002043193cb0d74cc173b3af7eb5800ad69c4e1fbac2a71c30"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9e24e7b6a68a51cc3b162d0339ae4e1263b253e887987d5c759652f5692b5efe"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e252d66276c992319ed6cd69a3ffa17538943954075051e992143ccbf6dc3d3e"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-win_amd64.whl", hash = "sha256:5d6e860edf877d4413e4a807e837d55e3a7c7df701e9d6943c06e460fa6c058f"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eea5f14933177ffe5c40b200f04f814258cc14b14a71024ad109f308e8bad414"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:824a1bfd0db96cc6bef2d1e52d9e0963f5bf653dd5bc3ab519a38f5e6f21c299"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a87e9eeb80ce8ec8c2783f29bce9a50bbcd2e2342a340f159c3326bf4697afa1"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91074f78a9f890af5f2c786691575b6b93a4967ad6b8c5a90101f7b8c1a91d9c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e05f6825f8db4428782135e6986fec79b139210398f3710ed4aa6ef41473c008"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f68ac2364a50d4cf9bb803b4341e83678668f1881a253e1224574921c69868c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7ac1785d67241d5074f8086705fa68e046becea27964267ab3abd392481d7773"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cd2a9f7f0d4dacc5b9ce7f0e767ae6cc64153264151f50698898c42cabffec0c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:3e4b0bb91da6f2238dbd4fbb4afc40dfb4f045bb611b92fce4d381b26413c686"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:74e498586b72fb819ca8ea82107747d0cb6e00ae685ea6d1ab3f929318a8ce2d"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:d4422af5232699f14b7266a754da49dc9bcd45eba244cf3812307934cd5d6679"}, +] + [[package]] name = "py-serializable" version = "1.0.3" @@ -1323,23 +1317,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" -[[package]] -name = "pydantic-extra-types" -version = "2.7.0" -description = "Extra Pydantic types." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_extra_types-2.7.0-py3-none-any.whl", hash = "sha256:ac01bbdaa4f85e4c4744a9792c5b0cfe61efa5028a0e670c3d8bfbf8b36c8543"}, - {file = "pydantic_extra_types-2.7.0.tar.gz", hash = "sha256:b9d9ddd755fa5960ec5a77cffcbd5d8796a0116e1dfc8f7c3a27fa0041693382"}, -] - -[package.dependencies] -pydantic = ">=2.5.2" - -[package.extras] -all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23)", "python-ulid (>=1,<2)", "python-ulid (>=1,<3)"] - [[package]] name = "pydantic-settings" version = "2.2.1" @@ -1441,20 +1418,6 @@ files = [ [package.extras] cli = ["click (>=5.0)"] -[[package]] -name = "python-multipart" -version = "0.0.9" -description = "A streaming multipart parser for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, - {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, -] - -[package.extras] -dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] - [[package]] name = "pyyaml" version = "6.0.1" @@ -1901,80 +1864,6 @@ files = [ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] -[[package]] -name = "ujson" -version = "5.9.0" -description = "Ultra fast JSON encoder and decoder for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, - {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, - {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, - {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, - {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, - {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, - {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, - {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, - {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, - {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, - {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, - {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, - {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, - {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, - {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, - {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, - {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, - {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, - {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, - {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, - {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, -] - [[package]] name = "uri-template" version = "1.3.0" @@ -1989,70 +1878,49 @@ files = [ [package.extras] dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"] -[[package]] -name = "uvicorn" -version = "0.29.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, - {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - [[package]] name = "uvloop" -version = "0.19.0" +version = "0.18.0" description = "Fast implementation of asyncio event loop on top of libuv" optional = false -python-versions = ">=3.8.0" -files = [ - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, - {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +python-versions = ">=3.7.0" +files = [ + {file = "uvloop-0.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1f354d669586fca96a9a688c585b6257706d216177ac457c92e15709acaece10"}, + {file = "uvloop-0.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:280904236a5b333a273292b3bcdcbfe173690f69901365b973fa35be302d7781"}, + {file = "uvloop-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad79cd30c7e7484bdf6e315f3296f564b3ee2f453134a23ffc80d00e63b3b59e"}, + {file = "uvloop-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99deae0504547d04990cc5acf631d9f490108c3709479d90c1dcd14d6e7af24d"}, + {file = "uvloop-0.18.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:edbb4de38535f42f020da1e3ae7c60f2f65402d027a08a8c60dc8569464873a6"}, + {file = "uvloop-0.18.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:54b211c46facb466726b227f350792770fc96593c4ecdfaafe20dc00f3209aef"}, + {file = "uvloop-0.18.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:25b714f07c68dcdaad6994414f6ec0f2a3b9565524fba181dcbfd7d9598a3e73"}, + {file = "uvloop-0.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1121087dfeb46e9e65920b20d1f46322ba299b8d93f7cb61d76c94b5a1adc20c"}, + {file = "uvloop-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74020ef8061678e01a40c49f1716b4f4d1cc71190d40633f08a5ef8a7448a5c6"}, + {file = "uvloop-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f4a549cd747e6f4f8446f4b4c8cb79504a8372d5d3a9b4fc20e25daf8e76c05"}, + {file = "uvloop-0.18.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6132318e1ab84a626639b252137aa8d031a6c0550250460644c32ed997604088"}, + {file = "uvloop-0.18.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:585b7281f9ea25c4a5fa993b1acca4ad3d8bc3f3fe2e393f0ef51b6c1bcd2fe6"}, + {file = "uvloop-0.18.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:61151cc207cf5fc88863e50de3d04f64ee0fdbb979d0b97caf21cae29130ed78"}, + {file = "uvloop-0.18.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c65585ae03571b73907b8089473419d8c0aff1e3826b3bce153776de56cbc687"}, + {file = "uvloop-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3d301e23984dcbc92d0e42253e0e0571915f0763f1eeaf68631348745f2dccc"}, + {file = "uvloop-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:680da98f12a7587f76f6f639a8aa7708936a5d17c5e7db0bf9c9d9cbcb616593"}, + {file = "uvloop-0.18.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:75baba0bfdd385c886804970ae03f0172e0d51e51ebd191e4df09b929771b71e"}, + {file = "uvloop-0.18.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ed3c28337d2fefc0bac5705b9c66b2702dc392f2e9a69badb1d606e7e7f773bb"}, + {file = "uvloop-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8849b8ef861431543c07112ad8436903e243cdfa783290cbee3df4ce86d8dd48"}, + {file = "uvloop-0.18.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:211ce38d84118ae282a91408f61b85cf28e2e65a0a8966b9a97e0e9d67c48722"}, + {file = "uvloop-0.18.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0a8f706b943c198dcedf1f2fb84899002c195c24745e47eeb8f2fb340f7dfc3"}, + {file = "uvloop-0.18.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:58e44650cbc8607a218caeece5a689f0a2d10be084a69fc32f7db2e8f364927c"}, + {file = "uvloop-0.18.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b8b7cf7806bdc745917f84d833f2144fabcc38e9cd854e6bc49755e3af2b53e"}, + {file = "uvloop-0.18.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:56c1026a6b0d12b378425e16250acb7d453abaefe7a2f5977143898db6cfe5bd"}, + {file = "uvloop-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:12af0d2e1b16780051d27c12de7e419b9daeb3516c503ab3e98d364cc55303bb"}, + {file = "uvloop-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b028776faf9b7a6d0a325664f899e4c670b2ae430265189eb8d76bd4a57d8a6e"}, + {file = "uvloop-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53aca21735eee3859e8c11265445925911ffe410974f13304edb0447f9f58420"}, + {file = "uvloop-0.18.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:847f2ed0887047c63da9ad788d54755579fa23f0784db7e752c7cf14cf2e7506"}, + {file = "uvloop-0.18.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6e20bb765fcac07879cd6767b6dca58127ba5a456149717e0e3b1f00d8eab51c"}, + {file = "uvloop-0.18.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e14de8800765b9916d051707f62e18a304cde661fa2b98a58816ca38d2b94029"}, + {file = "uvloop-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f3b18663efe0012bc4c315f1b64020e44596f5fabc281f5b0d9bc9465288559c"}, + {file = "uvloop-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6d341bc109fb8ea69025b3ec281fcb155d6824a8ebf5486c989ff7748351a37"}, + {file = "uvloop-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:895a1e3aca2504638a802d0bec2759acc2f43a0291a1dff886d69f8b7baff399"}, + {file = "uvloop-0.18.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d90858f32a852988d33987d608bcfba92a1874eb9f183995def59a34229f30d"}, + {file = "uvloop-0.18.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db1fcbad5deb9551e011ca589c5e7258b5afa78598174ac37a5f15ddcfb4ac7b"}, + {file = "uvloop-0.18.0.tar.gz", hash = "sha256:d5d1135beffe9cd95d0350f19e2716bc38be47d5df296d7cc46e3b7557c0d1ff"}, ] [package.extras] @@ -2192,88 +2060,7 @@ files = [ docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] tests = ["pytest", "pytest-cov"] -[[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, -] - [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "9adec4b3c1e9ffade2ba2b7694e6d9cf56e42752e6ff426fcba43078256f6c4d" +content-hash = "f1885d3734003c3ef4591b35fcd92c5b8738f3531a37562591705ae92e16df59" diff --git a/pyproject.toml b/pyproject.toml index c3ff31f0..8bd1be0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,14 +22,19 @@ packages = [ [tool.poetry.dependencies] python = "^3.10" -fastapi = {extras = ["all"], version = "^0.110.3"} +fastapi = "^0.110.3" sqlmodel = "^0.0.18" -psycopg = "^3.1.18" +psycopg = {extras = ["binary"], version = "^3.1.18"} alembic = "^1.13.1" +pydantic = "^2.7.1" +jinja2 = "^3.1.4" +pydantic-settings = "^2.2.1" +granian = {extras = ["reload"], version = "^1.3.1"} [tool.poetry.group.test.dependencies] pytest = "^8.1.1" coverage = "^7.5.1" +httpx = "^0.27.0" [tool.poetry.group.dev.dependencies] ruff = "^0.4.3" @@ -49,7 +54,7 @@ build-backend = "poetry.core.masonry.api" [tool.ruff] line-length = 120 target-version = "py311" -src = ["./tad"] +src = ["tad"] [tool.ruff.format] line-ending = "lf" @@ -70,6 +75,7 @@ pythonVersion = "3.11" typeCheckingMode = "strict" reportMissingImports = true reportMissingTypeStubs = true +reportUnnecessaryIsInstance = false [tool.coverage.run] branch = true @@ -88,3 +94,6 @@ title = "tad" testpaths = [ "tad/tests" ] +filterwarnings = [ + "ignore::UserWarning" +] From 5a9e0bbc6bb139b4b8f92e4faf75033c58d85656 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:02:44 +0000 Subject: [PATCH 11/55] Add codecoverage html and lcov generation to test script --- script/test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/test b/script/test index f88c0bea..1f8a3941 100755 --- a/script/test +++ b/script/test @@ -10,7 +10,8 @@ if [ $? -ne 0 ]; then fi coverage report - +coverage html +coverage lcov pyright $@ if [ $? -ne 0 ]; then echo "Typecheck failed" From 4eece342ccd948941cde7c348b58200007446e1e Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:03:25 +0000 Subject: [PATCH 12/55] Rename database scheme variable --- .env | 2 +- .gitignore | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 8d2b9d10..b21bb0ee 100644 --- a/.env +++ b/.env @@ -8,7 +8,7 @@ PROJECT_NAME="TAD" # TAD backend BACKEND_CORS_ORIGINS="http://localhost,https://localhost,http://127.0.0.1,https://127.0.0.1" SECRET_KEY=changethis -SQLALCHEMY_SCHEME="postgresql+psycopg" +APP_DATABASE_SCHEME="postgresql" APP_DATABASE_USER=tad APP_DATABASE_DB=tad APP_DATABASE_PASSWORD=changethis diff --git a/.gitignore b/.gitignore index cdcc118f..48f43e76 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ dist/ .coverage.* coverage.xml htmlcov/ +coverage.lcov # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ From 849ad428dfcaa615459e113be4155758ac55be26 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:04:17 +0000 Subject: [PATCH 13/55] Move dockerfile cmd to granian --- tad/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tad/Dockerfile b/tad/Dockerfile index 80a54940..2a3c9ecf 100644 --- a/tad/Dockerfile +++ b/tad/Dockerfile @@ -55,4 +55,4 @@ ENV PYTHONPATH=/app/ WORKDIR /app/ # change this to a usefull command -CMD ["python", "-m", "uvicorn", "--host", "0.0.0.0", "tad.main:app" ] +CMD ["python", "-m", "granian", "--host", "0.0.0.0","--interface","asgi","tad.main:app" ] From 2bf3467e991f3575ae7f80c6e4a7e5bb95803f3b Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:07:51 +0000 Subject: [PATCH 14/55] Add logging, masking and exceptions --- tad/core/config.py | 33 +++++++++++------ tad/core/db.py | 6 +-- tad/core/exceptions.py | 9 +++++ tad/core/logger.py | 9 +++++ tad/main.py | 57 ++++++++++++++++++++++++----- tad/middleware/routelogging.py | 19 ++++++++++ tad/models.py | 19 +--------- tad/utils/mask.py | 67 ++++++++++++++++++++++++++++++++++ 8 files changed, 175 insertions(+), 44 deletions(-) create mode 100644 tad/core/exceptions.py create mode 100644 tad/core/logger.py create mode 100644 tad/middleware/routelogging.py create mode 100644 tad/utils/mask.py diff --git a/tad/core/config.py b/tad/core/config.py index dc562c7d..51e82e94 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -11,6 +11,8 @@ from pydantic_core import MultiHostUrl from pydantic_settings import BaseSettings, SettingsConfigDict +from tad.core.exceptions import SettingsError + def parse_cors(v: Any) -> list[str] | str: if isinstance(v, str) and not v.startswith("["): @@ -21,8 +23,8 @@ def parse_cors(v: Any) -> list[str] | str: class Settings(BaseSettings): + # todo(berry): investigate yaml, toml or json file support for SettingsConfigDict model_config = SettingsConfigDict(env_file=".env", env_ignore_empty=True, extra="ignore") - API_V1_STR: str = "/api/v1" SECRET_KEY: str = secrets.token_urlsafe(32) DOMAIN: str = "localhost" @@ -31,34 +33,37 @@ class Settings(BaseSettings): @computed_field # type: ignore[misc] @property def server_host(self) -> str: - # Use HTTPS for anything other than local development if self.ENVIRONMENT == "local": return f"http://{self.DOMAIN}" return f"https://{self.DOMAIN}" BACKEND_CORS_ORIGINS: Annotated[list[AnyUrl] | str, BeforeValidator(parse_cors)] = [] + VERSION: str = "0.1.0" - PROJECT_NAME: str = "TAD" + LOGGING_LEVEL: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO" - SQLALCHEMY_SCHEME: str = "sqlite" + PROJECT_NAME: str = "TAD" + PROJECT_DESCRIPTION: str = "Transparency of Algorithmic Decision making" + # todo(berry): create submodel for database settings + APP_DATABASE_SCHEME: Literal["sqlite", "postgresql", "mysql", "oracle"] = "sqlite" APP_DATABASE_SERVER: str = "db" APP_DATABASE_PORT: int = 5432 APP_DATABASE_USER: str = "tad" APP_DATABASE_PASSWORD: str APP_DATABASE_DB: str = "tad" - SQLITE_FILE: str = "./database" + SQLITE_FILE: str = "//./database" @computed_field # type: ignore[misc] @property def SQLALCHEMY_DATABASE_URI(self) -> str: - if self.SQLALCHEMY_SCHEME == "sqlite": - return str(MultiHostUrl.build(scheme=self.SQLALCHEMY_SCHEME, host="", path=self.SQLITE_FILE)) + if self.APP_DATABASE_SCHEME == "sqlite": + return str(MultiHostUrl.build(scheme=self.APP_DATABASE_SCHEME, host="", path=self.SQLITE_FILE)) return str( MultiHostUrl.build( - scheme=self.SQLALCHEMY_SCHEME, + scheme=self.APP_DATABASE_SCHEME, username=self.APP_DATABASE_USER, password=self.APP_DATABASE_PASSWORD, host=self.APP_DATABASE_SERVER, @@ -69,13 +74,11 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: def _check_default_secret(self, var_name: str, value: str | None) -> None: if value == "changethis": - message = ( - f'The value of {var_name} is "changethis", ' "for security, please change it, at least for deployments." - ) + message = f'The value of {var_name} is "changethis", ' "for security, please change it" if self.ENVIRONMENT == "local": warnings.warn(message, stacklevel=1) else: - raise ValueError(message) + raise SettingsError(message) @model_validator(mode="after") def _enforce_non_default_secrets(self) -> Self: @@ -84,5 +87,11 @@ def _enforce_non_default_secrets(self) -> Self: return self + @model_validator(mode="after") + def _enforce_database_rules(self) -> Self: + if self.ENVIRONMENT != "local" and self.APP_DATABASE_SCHEME == "sqlite": + raise SettingsError("SQLite is not supported in production") # noqa: TRY003 + return self + settings = Settings() # type: ignore diff --git a/tad/core/db.py b/tad/core/db.py index ebfaf924..1b400351 100644 --- a/tad/core/db.py +++ b/tad/core/db.py @@ -1,9 +1,5 @@ -from sqlmodel import SQLModel, create_engine +from sqlmodel import create_engine from tad.core.config import settings engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, echo=True) - - -def init_db() -> None: - SQLModel.metadata.create_all(engine) diff --git a/tad/core/exceptions.py b/tad/core/exceptions.py new file mode 100644 index 00000000..4c32f360 --- /dev/null +++ b/tad/core/exceptions.py @@ -0,0 +1,9 @@ +class TADError(Exception): + pass + + +class SettingsError(TADError): + def __init__(self, message: str = "Settings error"): + self.message: str = message + exception_name: str = self.__class__.__name__ + super().__init__(f"{exception_name}: {self.message}") diff --git a/tad/core/logger.py b/tad/core/logger.py new file mode 100644 index 00000000..0efe6f43 --- /dev/null +++ b/tad/core/logger.py @@ -0,0 +1,9 @@ +import logging + +from tad.core.config import settings + + +def set_default_logging_setup(): + logging.basicConfig(level=settings.LOGGING_LEVEL, style="{", format="{asctime}({levelname},{name}): {message}") + # todo(berry): load from logging.config.dictConfig + # todo(berry): create RotatingFileHandler for logging to append to the default stream handler diff --git a/tad/main.py b/tad/main.py index d2246d55..e280f290 100644 --- a/tad/main.py +++ b/tad/main.py @@ -1,24 +1,61 @@ import logging +from contextlib import asynccontextmanager -from fastapi import FastAPI +from fastapi import FastAPI, Request +from fastapi.responses import HTMLResponse +from starlette.exceptions import HTTPException as StarletteHTTPException from starlette.middleware.cors import CORSMiddleware from tad.core.config import settings +from tad.core.logger import set_default_logging_setup +from tad.middleware.routelogging import RequestLoggingMiddleware +from tad.utils.mask import DataMasker +set_default_logging_setup() logger = logging.getLogger(__name__) +data_masker = DataMasker() -app = FastAPI(title=settings.PROJECT_NAME) -if settings.BACKEND_CORS_ORIGINS: - app.add_middleware( - CORSMiddleware, - allow_origins=[str(origin).strip("/") for origin in settings.BACKEND_CORS_ORIGINS], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], +@asynccontextmanager +async def lifespan(app: FastAPI): + logger.info(f"Starting {settings.PROJECT_NAME} version {settings.VERSION}") + logger.info(f"Settings: {data_masker.mask_data(settings.model_dump())}") + # todo(berry): setup database connection + yield + logger.info(f"Stopping application {settings.PROJECT_NAME} version {settings.VERSION}") + logging.shutdown() + + +app = FastAPI( + lifespan=lifespan, + title=settings.PROJECT_NAME, + summary=settings.PROJECT_DESCRIPTION, + version=settings.VERSION, + docs_url="/docs", + redoc_url=None, + default_response_class=HTMLResponse, +) + + +@app.exception_handler(StarletteHTTPException) +async def http_exception_handler(request: Request, exc: StarletteHTTPException): + return HTMLResponse( + status_code=exc.status_code, + content=f"

{exc.status_code}

{exc.detail}

", ) +app.add_middleware(RequestLoggingMiddleware) +app.add_middleware( + CORSMiddleware, + # todo(berry): what if BACKEND_CORS_ORIGINS is not set? + allow_origins=[str(origin).strip("/") for origin in settings.BACKEND_CORS_ORIGINS], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + @app.get("/") async def root(): - return {"message": "Hello World"} + return HTMLResponse(content="message - Hello World 2 sdfasdfsda") diff --git a/tad/middleware/routelogging.py b/tad/middleware/routelogging.py new file mode 100644 index 00000000..bc08cb5c --- /dev/null +++ b/tad/middleware/routelogging.py @@ -0,0 +1,19 @@ +import logging +import typing + +from starlette.middleware.base import BaseHTTPMiddleware +from starlette.requests import Request +from starlette.responses import Response + +RequestResponseEndpoint = typing.Callable[[Request], typing.Awaitable[Response]] + +logger = logging.getLogger(__name__) + + +class RequestLoggingMiddleware(BaseHTTPMiddleware): + async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: + # todo(berry): embed Request object in logging library + logger.debug(f"Request: {request.method} {request.url}") + response = await call_next(request) + logger.debug(f"Response: {response.status_code}") + return response diff --git a/tad/models.py b/tad/models.py index f0fd84cb..893e4abc 100644 --- a/tad/models.py +++ b/tad/models.py @@ -1,21 +1,6 @@ -from sqlmodel import Field, SQLModel +from sqlmodel import Field, SQLModel # type: ignore class Hero(SQLModel, table=True): - id: int | None = Field(default=None, primary_key=True) + id: int = Field(default=None, primary_key=True) name: str - secret_name: str - age: int | None = None - - -class UserBase(SQLModel): - email: str = Field(unique=True, index=True) - is_active: bool = True - is_superuser: bool = False - full_name: str | None = None - - -# Database model, database table inferred from class name -class User(UserBase, table=True): - id: int | None = Field(default=None, primary_key=True) - hashed_password: str diff --git a/tad/utils/mask.py b/tad/utils/mask.py new file mode 100644 index 00000000..f0b9ee9a --- /dev/null +++ b/tad/utils/mask.py @@ -0,0 +1,67 @@ +from typing import Any + + +class DataMasker: + def __init__(self, mask_value: str = "***MASKED***", mask_keywords: list[str] | None = None): + if mask_keywords is None: + mask_keywords = [] + self.mask_value = mask_value + + # default keywords to mask + self.keywords: list[str] = ["password", "secret", "database_uri"] + self.keywords.extend(mask_keywords or []) + + def mask_data( # noqa: C901 + self, data: str | list[Any] | dict[Any, Any] | set[Any] + ) -> str | list[Any] | dict[Any, Any] | set[Any]: + if isinstance(data, dict): + masked_dict: dict[str | int, str | int] = {} + for key, value in data.items(): + if isinstance(key, str): + for keyword in self.keywords: + if keyword in key.lower(): + masked_dict[key] = self.mask_value + break + else: + masked_dict[key] = value + else: + masked_dict[key] = value + return masked_dict + + elif isinstance(data, list): + masked_list: list[str | int] = [] + item: str | int + for item in data: + if isinstance(item, str): + for keyword in self.keywords: + if keyword in item.lower(): + masked_list.append(self.mask_value) + break + else: + masked_list.append(item) + else: + masked_list.append(item) + + return masked_list + elif isinstance(data, set): + masked_set: set[str | int] = set() + + item: str | int + for item in data: + if isinstance(item, str): + for keyword in self.keywords: + if keyword in item.lower(): + masked_set.add(self.mask_value) + break + else: + masked_set.add(item) + else: + masked_set.add(item) + return masked_set + elif isinstance(data, str): + for keyword in self.keywords: + if keyword in data.lower(): + return self.mask_value + return data + else: + raise TypeError("Unsupported data type. Function supports dict, list, or set.") # noqa: TRY003 From 63c49a54d3592e981a555aeeffda5f222652658c Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:19:03 +0000 Subject: [PATCH 15/55] Add tests for masking, logging and exceptions --- tad/tests/conftest.py | 25 +++++++++ tad/tests/core/test_config.py | 88 +++++++++++++++++++++++++++++++ tad/tests/core/test_exceptions.py | 9 ++++ tad/tests/core/test_logger.py | 17 ++++++ tad/tests/test_main.py | 17 ++++++ tad/tests/test_variables.py | 0 tad/tests/utils/test_mask.py | 52 ++++++++++++++++++ tad/utils/mask.py | 4 +- 8 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 tad/tests/conftest.py create mode 100644 tad/tests/core/test_config.py create mode 100644 tad/tests/core/test_exceptions.py create mode 100644 tad/tests/core/test_logger.py create mode 100644 tad/tests/test_variables.py create mode 100644 tad/tests/utils/test_mask.py diff --git a/tad/tests/conftest.py b/tad/tests/conftest.py new file mode 100644 index 00000000..2115d941 --- /dev/null +++ b/tad/tests/conftest.py @@ -0,0 +1,25 @@ +import os +from collections.abc import Generator + +import pytest +from fastapi.testclient import TestClient + +from tad.main import app + +# todo(berry): add database fixtures + + +@pytest.fixture(scope="module") +def client() -> Generator[TestClient, None, None]: + with TestClient(app, raise_server_exceptions=True) as c: + c.timeout = 3 + yield c + + +@pytest.fixture(autouse=True) +def setup_basic_environmental_variables() -> Generator[None, None, None]: # noqa: PT004 + original_environ = dict(os.environ) + os.environ["APP_DATABASE_PASSWORD"] = "changethis" # noqa: S105 + yield + os.environ.clear() + os.environ.update(original_environ) diff --git a/tad/tests/core/test_config.py b/tad/tests/core/test_config.py new file mode 100644 index 00000000..d22b3da8 --- /dev/null +++ b/tad/tests/core/test_config.py @@ -0,0 +1,88 @@ +import os + +import pytest +from pydantic import AnyUrl +from tad.core.config import Settings +from tad.core.exceptions import SettingsError + + +def test_default_settings(): + settings = Settings(_env_file="nonexisitingfile") # type: ignore + + assert settings.DOMAIN == "localhost" + assert settings.ENVIRONMENT == "local" + assert settings.server_host == "http://localhost" + assert [] == settings.BACKEND_CORS_ORIGINS + assert settings.VERSION == "0.1.0" + assert settings.LOGGING_LEVEL == "INFO" + assert settings.PROJECT_NAME == "TAD" + assert settings.PROJECT_DESCRIPTION == "Transparency of Algorithmic Decision making" + assert settings.APP_DATABASE_SCHEME == "sqlite" + assert settings.APP_DATABASE_SERVER == "db" + assert settings.APP_DATABASE_PORT == 5432 + assert settings.APP_DATABASE_USER == "tad" + assert settings.APP_DATABASE_DB == "tad" + assert settings.SQLITE_FILE == "//./database" + assert settings.SQLALCHEMY_DATABASE_URI == "sqlite://///database" + + +def test_environment_settings(): + os.environ["DOMAIN"] = "google.com" + os.environ["ENVIRONMENT"] = "production" + os.environ["PROJECT_NAME"] = "TAD2" + os.environ["BACKEND_CORS_ORIGINS"] = "http://google.com,https://google.com" + os.environ["SECRET_KEY"] = "mysecret" # noqa: S105 + os.environ["APP_DATABASE_SCHEME"] = "postgresql" + os.environ["APP_DATABASE_USER"] = "tad2" + os.environ["APP_DATABASE_DB"] = "tad2" + os.environ["APP_DATABASE_PASSWORD"] = "mypassword" # noqa: S105 + settings = Settings(_env_file="nonexisitingfile") # type: ignore + + assert settings.SECRET_KEY == "mysecret" # noqa: S105 + assert settings.DOMAIN == "google.com" + assert settings.ENVIRONMENT == "production" + assert settings.server_host == "https://google.com" + assert [AnyUrl("http://google.com"), AnyUrl("https://google.com")] == settings.BACKEND_CORS_ORIGINS + assert settings.VERSION == "0.1.0" + assert settings.LOGGING_LEVEL == "INFO" + assert settings.PROJECT_NAME == "TAD2" + assert settings.PROJECT_DESCRIPTION == "Transparency of Algorithmic Decision making" + assert settings.APP_DATABASE_SCHEME == "postgresql" + assert settings.APP_DATABASE_SERVER == "db" + assert settings.APP_DATABASE_PORT == 5432 + assert settings.APP_DATABASE_USER == "tad2" + assert settings.APP_DATABASE_PASSWORD == "mypassword" # noqa: S105 + assert settings.APP_DATABASE_DB == "tad2" + assert settings.SQLITE_FILE == "//./database" + assert settings.SQLALCHEMY_DATABASE_URI == "postgresql://tad2:mypassword@db:5432/tad2" + + +def test_environment_settings_cors_list(): + os.environ["BACKEND_CORS_ORIGINS"] = "[http://google.com,https://google.com]" + + settings = Settings(_env_file="nonexisitingfile") # type: ignore + + assert settings.BACKEND_CORS_ORIGINS == "[http://google.com,https://google.com]" + + +def test_environment_settings_cors_error(): + os.environ["BACKEND_CORS_ORIGINS"] = "1234" + with pytest.raises(ValueError, match="1234"): + _settings = Settings(_env_file="nonexisitingfile") # type: ignore + + +def test_environment_settings_production_sqlite_error(): + os.environ["ENVIRONMENT"] = "production" + os.environ["APP_DATABASE_SCHEME"] = "sqlite" + os.environ["APP_DATABASE_PASSWORD"] = "32452345432" # noqa: S105 + with pytest.raises(SettingsError) as e: + _settings = Settings(_env_file="nonexisitingfile") # type: ignore + + assert e.value.message == "SQLite is not supported in production" + + +def test_environment_settings_production_nopassword_error(): + os.environ["ENVIRONMENT"] = "production" + + with pytest.raises(SettingsError): + _settings = Settings(_env_file="nonexisitingfile") # type: ignore diff --git a/tad/tests/core/test_exceptions.py b/tad/tests/core/test_exceptions.py new file mode 100644 index 00000000..62891784 --- /dev/null +++ b/tad/tests/core/test_exceptions.py @@ -0,0 +1,9 @@ +import pytest +from tad.core.exceptions import SettingsError + + +def test_environment_settings(): + with pytest.raises(SettingsError) as exc_info: + raise SettingsError("Wrong settings") # noqa: TRY003 + + assert exc_info.value.message == "Wrong settings" diff --git a/tad/tests/core/test_logger.py b/tad/tests/core/test_logger.py new file mode 100644 index 00000000..b510ac50 --- /dev/null +++ b/tad/tests/core/test_logger.py @@ -0,0 +1,17 @@ +import logging + +import pytest +from tad.core.logger import set_default_logging_setup + + +def test_set_default_logging_setup(caplog: pytest.LogCaptureFixture): + set_default_logging_setup() + + logger = logging.getLogger("tad.main") + + message = "This is a test log message" + logger.info(message) + logger.debug(message) + + assert len(caplog.records) == 1 + assert caplog.records[0].message == message diff --git a/tad/tests/test_main.py b/tad/tests/test_main.py index e69de29b..c084568f 100644 --- a/tad/tests/test_main.py +++ b/tad/tests/test_main.py @@ -0,0 +1,17 @@ +from fastapi.testclient import TestClient + + +def test_get_root(client: TestClient) -> None: + response = client.get( + "/", + ) + assert response.status_code == 200 + assert response.headers["content-type"] == "text/html; charset=utf-8" + + +def test_get_non_exisiting(client: TestClient) -> None: + response = client.get( + "/pathdoesnotexist/", + ) + assert response.status_code == 404 + assert response.headers["content-type"] == "text/html; charset=utf-8" diff --git a/tad/tests/test_variables.py b/tad/tests/test_variables.py new file mode 100644 index 00000000..e69de29b diff --git a/tad/tests/utils/test_mask.py b/tad/tests/utils/test_mask.py new file mode 100644 index 00000000..f72c3f22 --- /dev/null +++ b/tad/tests/utils/test_mask.py @@ -0,0 +1,52 @@ +from tad.utils.mask import DataMasker + + +def test_mask_dict(): + # given + data: dict[str | int, str] = {1: "value1", "MY_PASSWORD": "value2", "key3": "value3"} + data_masker = DataMasker() + + # when + masked_data = data_masker.mask_data(data) + + # then + assert masked_data == {1: "value1", "MY_PASSWORD": "***MASKED***", "key3": "value3"} + + +def test_mask_set(): + # given + data: set[str | int] = {"value1", "password", "value3", 4} + data_masker = DataMasker() + + # when + masked_data = data_masker.mask_data(data) + + # then + assert masked_data == {"value1", "***MASKED***", "value3", 4} + + +def test_mask_list(): + # given + data: list[str | int] = [1, "mysecret", "notimportant"] + data_masker = DataMasker(mask_value="******") + + # when + masked_data = data_masker.mask_data(data) + + # then + assert masked_data == [1, "******", "notimportant"] + + +def test_mask_string(): + # given + data: str = "my_token" + data_nosecret: str = "my_notimportant" + data_masker = DataMasker(mask_keywords=["token"]) + + # when + masked_data = data_masker.mask_data(data) + masked_data_nosecret = data_masker.mask_data(data_nosecret) + + # then + assert masked_data == "***MASKED***" + assert masked_data_nosecret == "my_notimportant" diff --git a/tad/utils/mask.py b/tad/utils/mask.py index f0b9ee9a..f4613e27 100644 --- a/tad/utils/mask.py +++ b/tad/utils/mask.py @@ -58,10 +58,8 @@ def mask_data( # noqa: C901 else: masked_set.add(item) return masked_set - elif isinstance(data, str): + else: for keyword in self.keywords: if keyword in data.lower(): return self.mask_value return data - else: - raise TypeError("Unsupported data type. Function supports dict, list, or set.") # noqa: TRY003 From 98bf40eafa3464caf097898c458e8e8a209aeb4a Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:19:28 +0000 Subject: [PATCH 16/55] Add lcov generation to code coverage --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 43e93d22..9a87be23 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -41,7 +41,7 @@ "label": "Code Coverage", "detail": "Generate code coverage report.", "type": "shell", - "command": "poetry run coverage report", + "command": "poetry run coverage report && poetry run coverage html && poetry run coverage lcov", "group": "test", "presentation": { "reveal": "always", From 76a2f2e9471f310507320c4df8ae97b9555ce5d8 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:19:43 +0000 Subject: [PATCH 17/55] Add favicon --- tad/static/favicon.ico | Bin 0 -> 15406 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tad/static/favicon.ico diff --git a/tad/static/favicon.ico b/tad/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4c57aaac7adb119830b6b9183850f4e9d416b72e GIT binary patch literal 15406 zcmeHO3s_Xu7CvS#wTG2f*IwwoU-@ZfzDm$uUluB2rQ*Grb~P2#Za4FBwd>aVh{a-V_4Gn?tzAB81Xhay6lF!A*Vclns+G(v*IVHx^M(^ zrMaNGECtmiDXUW(@kqv(f4;xXDS7<8#PHyhqyo89XFTY$R)OL422iI=Vt!aV)~Stn z>ZI|keR9$TIq5#I*7)7Tq$1X4D%uMvJG3B*R)SG+(WP&~I<*mxWXLAzpzgPw*BUIm zex0HMhCNrzC|7C(&AzFOax;ytb}h>A`uC%5=QYMdyuMj>6gI{vVadEI&?!n$j{Il{myHR2cO*mk4%*OVrBi@Jpl}7H-V|(d-OYv$x%Disf~DQlwta)L%K8u z)XC%fVw;26K>4Oh70F7*KF5zADzl?s`s~2x1E4vSv+AXVLS3qVJMocD3DbJ4U%YIF zarVDw!pwOex)HT4UKVZ?P4BVpE0p!r=kG`5LC@f95Dh$Fz5HBCy@iPC!C7`ua4tN< z%e7TW^GpEJt4K6I`Mac1eICH~{>k5zqK4uO$#Llsz3$0~3Nm_|r+&@0jHn@|)QA`E zJF;}}V>jX_bHz|7-4Y6ATSI~BH)7*3#yoxHphsJ(Oa88YJKFU=Q2jU`KB?D_37Xa$0LJAUK9myDcLd#{E=e&+pd1|zWI`P{&b1= zizs+=Ci=>X9^8R^3oo2M=J2+Dtx_o-H(TgaSQq+N;?~=^zIwA2niN)7ppf=)>O#Ueolw7W7x@1F*wmkhm9B<0+u%fnG$5_LTt{Y4Y zld%7^CZucA8l`KEI%z~1)K=S|L0^gXoa<$$-SMnViM&Z8%Utqb$0?Z&w!UnP6W{+X z=(FQ$!}d(xjmul}pggS^b@>I?5bc1aEMB^(D-rz;M5yc}O%EwzFbZTawT#26g)=HFB( z0f)l@pUqUbY>pTxgQ5Zp*z0v}&y=1owOzVkfsrV~`(F5=g4rNj-u$EQBOPLl1G}y_ zX+6iEX73beFxEi5#SZ%=b+C1V$~mSGlObQ^vd7z{HgpE1I95q^AL@`U*&tiIoa_0c z&kiwK_B*xrPI%4JKgnwk%!K-i)8Mc-V00bqy)xvkb=l%=@+I-;-6L7jAziY;^Qq+h z_x>JU<=_84$?$mpBv3_4_Im%Vlky(+;R822LYtm-CW|_r?aaSqpM!jnPu@RY;_vNW zef@(NKH>d&miSJGBHw!q1oND?el@49@%P~_ zcs98np7_QDUE+;^`y`AW!aB7Pk7Ppgz&PqO#16}Y`~BH&3GdA;co6MQMjTtw_F1gg zV%qY`aMwNZ(Q~p z8?kdImInr9EDu^IjSAZQ`=G#h+?Nv_{z3Sz*dk#@U;>9@U z&^sVqHe@R9)#-d;YHmBlxMJH|F5EnRzN9|1#dDxNR*e62!cLbI_k5+x0~el%3d)o& z4?33<{j!eyw1o0vUvrHa9>Y#3&z#iGIPMr(n%QJ4R!LLgG8_1;_Kz3dV^+pZUo3xC~dKHvi zIt#@G*>Lsp&#rdjtEmgY&NCg-HP+Q$E*letWvdib3d%;%HnwQ`c00>|#Fh^06*=#?Seazg_&# zXW{tySDWFd_g8T|hUH+aW`k?I-thSMHGa^x?A!^cIKCQGyWRJBS`&YIe$)r$o++S7 z`xLZ?7ckObS$l9U)Zn~ICEre^I&D5{Cmz*>yxN2FTsmZf=V{XBFdyVgICfe)emrg6 zjQiHc|7Kto`uwBge_RY5`tOGMxCgp5M>4NM*!NqZepd2HHDN>gY`|XV=)U6NILs9d z$NZ6l_N#wPT5{d{Zq75<)+9M*{ql6f@c48ud(Ct6TX2G_`rof0&eRVV|eq4MDtJn z@N0jZ3pQ;TrDMQmbAU$O05w%MFz6dFC*aHrn6DfH^`7^=`e%JenFu7~u-jPv#GtoB zE!xni8kkM8O*t^~<<@6>jzauTY^pCL%$yk7-xX*6bEjBCVK`24u;2~$2+^+AZg+eWjaQ>?!5j%ae4^=ho5qM zj*c<9ILveYa!;%Q{NXqL{2jBathM1w?v9<$sZAeK&hlC$SeO1EH{TThx?_u&4a$AZ z{=3|ji{tgF4DMHqW75jxOc!>!+3<)z5ay0C`b(H6H8zFceB~gsd0KF;Q0SNHVXu`! zRfW^HK4*=aZ^ExX6~k;)E3EK-$hBOqr=63>oX^oLJK|=;8-5)327CrpP2o2c;!BTL z@guv4!3 zo+ie>F5^qroblN##dW!BdhVOos+eCp&ZlZ`ImI8QfZb|jGCRIjL$Jp|^QHZ6e);%ON#6)2gL4@5cS3L(i^ZrHWw3ZoCCd?`0P{uPH=(X){Bi84I>s4> zbK!6YK7&P%Z8RJG&HuWSE1lmhv~M!n*;tq1c%rieRx3`3*iQL)&4CZy{0cE>_D*Fo z3?qI+NVnv&1uErMW|R6x-$U}{eh$KY{>S;#(s8wJ5f=w{Ilp7juGfx+PyOL19D1~C ztGkMGxS7R~<+JgdV6WFRT-vlxd}8POK{EAFe~3P0JS_!{yOz>Mdj&BbXzXAsyqYk%Ke)DvP+@0rA6OuFVPX*lNg zF`L>{=UH_(*+yL<{(ie*qHF!oSQ)S#y7U*rY3zsnN6C_=bKSU(bAG3U=UqH-dg8wB z9(CzkN4Boxx!cN+EOHPwTz{THAJ29ybN?H9dx!c;;=@M>=1DXGp?*BX;KiaP2gbu*JB~T`b@Bmi6j)qv2){`nD;> zW3Q-^#x3K|B)cQ^eHRhI-Il)O#i6_A_A38=PH(swv~zCn+7n9xgT!HB?R_Kl%~?CC z+d~P{11_}Hd5@Obi4PAb+c4qDR-IW8P3=Ax&zTrn>bv#w-HoEDPvLi}brR;Q^W))MATm5^MRJva-l``-gE5L({^&<_1Qi+z}nbT90F;+%4?p K|Hc2f5%@2((6nLz literal 0 HcmV?d00001 From d03903ed2b36fac216b563586afaa4a0edd9d9a8 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:45:06 +0000 Subject: [PATCH 18/55] Fix python3.10 problem with Self type --- .gitignore | 3 +++ tad/core/config.py | 9 ++++++--- tad/tests/conftest.py | 3 +-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 48f43e76..d8ab9c17 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ __pypackages__/ #mypyr .mypy_cache/ + +# macos +.DS_Store diff --git a/tad/core/config.py b/tad/core/config.py index 51e82e94..3fe570bc 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -1,6 +1,6 @@ import secrets import warnings -from typing import Annotated, Any, Literal, Self +from typing import Annotated, Any, Literal, TypeVar from pydantic import ( AnyUrl, @@ -13,6 +13,9 @@ from tad.core.exceptions import SettingsError +# Self type is not available in Python 3.10 so create our own with TypeVar +SelfSettings = TypeVar("SelfSettings", bound="Settings") + def parse_cors(v: Any) -> list[str] | str: if isinstance(v, str) and not v.startswith("["): @@ -81,14 +84,14 @@ def _check_default_secret(self, var_name: str, value: str | None) -> None: raise SettingsError(message) @model_validator(mode="after") - def _enforce_non_default_secrets(self) -> Self: + def _enforce_non_default_secrets(self: SelfSettings) -> SelfSettings: self._check_default_secret("SECRET_KEY", self.SECRET_KEY) self._check_default_secret("APP_DATABASE_PASSWORD", self.APP_DATABASE_PASSWORD) return self @model_validator(mode="after") - def _enforce_database_rules(self) -> Self: + def _enforce_database_rules(self: SelfSettings) -> SelfSettings: if self.ENVIRONMENT != "local" and self.APP_DATABASE_SCHEME == "sqlite": raise SettingsError("SQLite is not supported in production") # noqa: TRY003 return self diff --git a/tad/tests/conftest.py b/tad/tests/conftest.py index 2115d941..8bc5bfe8 100644 --- a/tad/tests/conftest.py +++ b/tad/tests/conftest.py @@ -6,9 +6,8 @@ from tad.main import app -# todo(berry): add database fixtures - +# todo(berry): add database fixtures @pytest.fixture(scope="module") def client() -> Generator[TestClient, None, None]: with TestClient(app, raise_server_exceptions=True) as c: From 734c66041a20e23c49ea5df39b8cb94f499d499a Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 13:51:06 +0000 Subject: [PATCH 19/55] Fix docker build --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8267c31..207dd234 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -170,6 +170,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . + file: ./tad/Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} From 292cf89ab535aa7dc6cb55bbbe225a20c025b6cb Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 14:31:53 +0000 Subject: [PATCH 20/55] Add sonarcloud exclusion of tests and alembic --- .github/workflows/codeql.yml | 32 -------------------------------- README.md | 1 + sonar-project.properties | 4 +++- 3 files changed, 4 insertions(+), 33 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index f61a4d06..00000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - analyze: - name: Analyze (python) - runs-on: 'ubuntu-latest' - timeout-minutes: 360 - permissions: - security-events: write - packages: read - actions: read - contents: read - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: python - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:python" diff --git a/README.md b/README.md index e72892f8..d6cd7bad 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/minbzk/tad/total) ![GitHub License](https://img.shields.io/github/license/minbzk/tad) ![GitHub Release](https://img.shields.io/github/v/release/minbzk/tad) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ai-validation-team_tad&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ai-validation-team_tad) TAD is a modern tool to apply technical and non-technical tests for an AI model. diff --git a/sonar-project.properties b/sonar-project.properties index bdd24016..d05f6bf2 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -12,6 +12,8 @@ sonar.sources=. # Encoding of the source code. Default is default system encoding sonar.sourceEncoding=UTF-8 -sonar.python.version=3.11 +sonar.python.version=3.10,3.11,3.12 sonar.python.coverage.reportPaths=coverage.xml + +sonar.coverage.exclusions="tad/alembic/*","tad/tests/*" From bca492c804e0f6a73a5322ee5c0736e981685e57 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 14:42:44 +0000 Subject: [PATCH 21/55] Add package cleaner for github --- .github/workflows/package.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/package.yml diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml new file mode 100644 index 00000000..b4e2c30e --- /dev/null +++ b/.github/workflows/package.yml @@ -0,0 +1,20 @@ +name: "Clean up package versions" +on: + schedule: + - cron: "0 4 * * *" + workflow_dispatch: + +jobs: + clenaup-package-versions: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + packages: write + steps: + - uses: actions/delete-package-versions@v5 + with: + package-name: 'tad' + package-type: 'container' + min-versions-to-keep: 0 + ignore-versions: 'main' + delete-only-untagged-versions: true From 8891b873e5f04317252dae82469ffe8d5b937b21 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 14:58:20 +0000 Subject: [PATCH 22/55] Set correct labels for docker image --- .github/workflows/ci.yml | 3 +++ tad/Dockerfile | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 207dd234..d9d1ad9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,6 +165,8 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + env: + DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - name: Build and push Docker image uses: docker/build-push-action@v5 @@ -174,6 +176,7 @@ jobs: push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + annotations: ${{ steps.meta.outputs.annotations }} platforms: linux/amd64,linux/arm64,darwin/amd64 - name: Run Trivy vulnerability scanner diff --git a/tad/Dockerfile b/tad/Dockerfile index 2a3c9ecf..809ad599 100644 --- a/tad/Dockerfile +++ b/tad/Dockerfile @@ -4,7 +4,10 @@ FROM --platform=$BUILDPLATFORM python:${PYTHON_VERSION} AS project-base LABEL maintainer=ai-validatie@minbzk.nl \ organization=MinBZK \ - license=EUPL-1.2 + license=EUPL-1.2 \ + org.opencontainers.image.description="Transparency of Algorithmic Decision making" \ + org.opencontainers.image.source=https://github.com/MinBZK/tad \ + org.opencontainers.image.licenses=EUPL-1.2 ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ From e5be9cac0080de7ea0edf552cd193c2f230e5a51 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 15:21:57 +0000 Subject: [PATCH 23/55] Add badges for sonarcloud --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d6cd7bad..7b72eca4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Transparency of Algorithmic Decision making (TAD) -![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/minbzk/tad/ci.yml) -![Codecov (with branch)](https://img.shields.io/codecov/c/github/minbzk/tad/main) +![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/minbzk/tad/ci.yml?label=CI) +![Sonar Coverage](https://img.shields.io/sonar/coverage/ai-validation-team_tad?server=https%3A%2F%2Fsonarcloud.io&label=coverage(sonar)) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ai-validation-team_tad&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ai-validation-team_tad) ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/minbzk/tad/total) +![GitHub Release](https://img.shields.io/github/v/release/minbzk/tad?include_prereleases&sort=semver) ![GitHub License](https://img.shields.io/github/license/minbzk/tad) -![GitHub Release](https://img.shields.io/github/v/release/minbzk/tad) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ai-validation-team_tad&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ai-validation-team_tad) TAD is a modern tool to apply technical and non-technical tests for an AI model. From b21bd45abcc8b281056ffc0c3a957c5f8cbcbf46 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 15:52:41 +0000 Subject: [PATCH 24/55] Renamed actions --- .github/workflows/ci.yml | 6 ++-- ...ckage.yml => cleanup-package-schedule.yml} | 4 +-- .github/workflows/first-interaction.yml | 2 +- .github/workflows/pr-close.yml | 33 +++++++++++++++++++ .../{stale.yml => stale-pr-schedule.yml} | 2 +- 5 files changed, 39 insertions(+), 8 deletions(-) rename .github/workflows/{package.yml => cleanup-package-schedule.yml} (86%) create mode 100644 .github/workflows/pr-close.yml rename .github/workflows/{stale.yml => stale-pr-schedule.yml} (97%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9d1ad9a..f5d8c48d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI +name: continuous-integration on: push: @@ -173,14 +173,13 @@ jobs: with: context: . file: ./tad/Dockerfile - push: ${{ github.event_name != 'pull_request' }} + push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} platforms: linux/amd64,linux/arm64,darwin/amd64 - name: Run Trivy vulnerability scanner - if: github.event_name != 'pull_request' uses: aquasecurity/trivy-action@master with: image-ref: ${{ steps.meta.outputs.tags }} @@ -194,7 +193,6 @@ jobs: TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Upload Trivy scan results to GitHub Security tab - if: github.event_name != 'pull_request' uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/package.yml b/.github/workflows/cleanup-package-schedule.yml similarity index 86% rename from .github/workflows/package.yml rename to .github/workflows/cleanup-package-schedule.yml index b4e2c30e..0f86154a 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/cleanup-package-schedule.yml @@ -1,11 +1,11 @@ -name: "Clean up package versions" +name: cleanup-package-schedule on: schedule: - cron: "0 4 * * *" workflow_dispatch: jobs: - clenaup-package-versions: + cleanup-package-versions: runs-on: ubuntu-latest timeout-minutes: 10 permissions: diff --git a/.github/workflows/first-interaction.yml b/.github/workflows/first-interaction.yml index 4dfd169a..edad6119 100644 --- a/.github/workflows/first-interaction.yml +++ b/.github/workflows/first-interaction.yml @@ -1,4 +1,4 @@ -name: Greetings +name: first-interaction on: [pull_request, issues] diff --git a/.github/workflows/pr-close.yml b/.github/workflows/pr-close.yml new file mode 100644 index 00000000..735bcbbf --- /dev/null +++ b/.github/workflows/pr-close.yml @@ -0,0 +1,33 @@ +name: pr-close + +on: + pull_request: + types: + - closed + workflow_dispatch: + inputs: + pr_number: + description: 'PR Number' + required: true + type: string + +jobs: + cleanup-package-versions: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + packages: write + steps: + - uses: actions/delete-package-versions@v5 + if: ${{ github.event_name != 'pull_request' }} + with: + package-name: 'tad' + package-type: 'container' + package-version-ids: ${{ inputs.pr_number }} + + - uses: actions/delete-package-versions@v5 + if: ${{ github.event_name == 'pull_request' }} + with: + package-name: 'tad' + package-type: 'container' + package-version-ids: PR-${{ github.event.number }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale-pr-schedule.yml similarity index 97% rename from .github/workflows/stale.yml rename to .github/workflows/stale-pr-schedule.yml index 35b37843..a1c2395f 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale-pr-schedule.yml @@ -1,4 +1,4 @@ -name: "Close stale" +name: stale-shedule on: schedule: - cron: "0 4 * * *" From 4a43a2379c294ecc092f5c06ce420970de52f01f Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Wed, 8 May 2024 15:54:28 +0000 Subject: [PATCH 25/55] Rename ci to test --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b72eca4..eeb585e3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Transparency of Algorithmic Decision making (TAD) -![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/minbzk/tad/ci.yml?label=CI) +![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/minbzk/tad/ci.yml?label=tests) ![Sonar Coverage](https://img.shields.io/sonar/coverage/ai-validation-team_tad?server=https%3A%2F%2Fsonarcloud.io&label=coverage(sonar)) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ai-validation-team_tad&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ai-validation-team_tad) ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/minbzk/tad/total) From 3d94c672e31eda0dcdce986e121dd4755eadd19d Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:23:49 +0000 Subject: [PATCH 26/55] Rename middleware --- tad/middleware/{routelogging.py => route_logging.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tad/middleware/{routelogging.py => route_logging.py} (100%) diff --git a/tad/middleware/routelogging.py b/tad/middleware/route_logging.py similarity index 100% rename from tad/middleware/routelogging.py rename to tad/middleware/route_logging.py From 451f4e5eeec7f995fd0250ed2c22875b5d428f0b Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:25:06 +0000 Subject: [PATCH 27/55] Add exception handler file --- tad/core/exception_handlers.py | 18 +++++++++++ tad/main.py | 58 +++++++++++++++++++--------------- 2 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 tad/core/exception_handlers.py diff --git a/tad/core/exception_handlers.py b/tad/core/exception_handlers.py new file mode 100644 index 00000000..7bdb6d39 --- /dev/null +++ b/tad/core/exception_handlers.py @@ -0,0 +1,18 @@ +from fastapi import Request, status +from fastapi.exceptions import RequestValidationError +from fastapi.responses import HTMLResponse +from starlette.exceptions import HTTPException as StarletteHTTPException + + +async def http_exception_handler(request: Request, exc: StarletteHTTPException): + return HTMLResponse( + status_code=exc.status_code, + content=f"

{exc.status_code}

{exc.detail}

", + ) + + +async def validation_exception_handler(_request: Request, _exc: RequestValidationError): + return HTMLResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content="

{status.HTTP_400_BAD_REQUEST}

Invalid Request

", + ) diff --git a/tad/main.py b/tad/main.py index e280f290..ea21ff00 100644 --- a/tad/main.py +++ b/tad/main.py @@ -2,60 +2,66 @@ from contextlib import asynccontextmanager from fastapi import FastAPI, Request +from fastapi.exceptions import RequestValidationError from fastapi.responses import HTMLResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates from starlette.exceptions import HTTPException as StarletteHTTPException -from starlette.middleware.cors import CORSMiddleware +from tad.api.main import api_router from tad.core.config import settings -from tad.core.logger import set_default_logging_setup -from tad.middleware.routelogging import RequestLoggingMiddleware -from tad.utils.mask import DataMasker +from tad.core.exception_handlers import ( + http_exception_handler as tad_http_exception_handler, +) +from tad.core.exception_handlers import ( + validation_exception_handler as tad_validation_exception_handler, +) +from tad.core.log import configure_logging +from tad.middleware.route_logging import RequestLoggingMiddleware +from tad.utils.mask import Mask + +configure_logging(settings.LOGGING_LEVEL, settings.LOGGING_CONFIG) -set_default_logging_setup() logger = logging.getLogger(__name__) -data_masker = DataMasker() +mask = Mask(mask_keywords=["database_uri"]) +# todo(berry): move lifespan to own file @asynccontextmanager async def lifespan(app: FastAPI): logger.info(f"Starting {settings.PROJECT_NAME} version {settings.VERSION}") - logger.info(f"Settings: {data_masker.mask_data(settings.model_dump())}") + logger.info(f"Settings: {mask.secrets(settings.model_dump())}") # todo(berry): setup database connection yield logger.info(f"Stopping application {settings.PROJECT_NAME} version {settings.VERSION}") logging.shutdown() +templates = Jinja2Templates(directory="templates") + app = FastAPI( lifespan=lifespan, title=settings.PROJECT_NAME, summary=settings.PROJECT_DESCRIPTION, version=settings.VERSION, - docs_url="/docs", - redoc_url=None, + openapi_url=None, default_response_class=HTMLResponse, + redirect_slashes=False, ) +app.add_middleware(RequestLoggingMiddleware) + +app.mount("/static", StaticFiles(directory=settings.STATIC_DIR), name="static") + @app.exception_handler(StarletteHTTPException) -async def http_exception_handler(request: Request, exc: StarletteHTTPException): - return HTMLResponse( - status_code=exc.status_code, - content=f"

{exc.status_code}

{exc.detail}

", - ) +async def http_exception_handler(request: Request, exc: StarletteHTTPException) -> HTMLResponse: + return await tad_http_exception_handler(request, exc) -app.add_middleware(RequestLoggingMiddleware) -app.add_middleware( - CORSMiddleware, - # todo(berry): what if BACKEND_CORS_ORIGINS is not set? - allow_origins=[str(origin).strip("/") for origin in settings.BACKEND_CORS_ORIGINS], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request: Request, exc: RequestValidationError) -> HTMLResponse: + return await tad_validation_exception_handler(request, exc) -@app.get("/") -async def root(): - return HTMLResponse(content="message - Hello World 2 sdfasdfsda") +app.include_router(api_router) From 7ee42a9b249728cf26ac4fd1df339f7d22a5e2b5 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:25:37 +0000 Subject: [PATCH 28/55] Move migrations --- tad/alembic.ini => alembic.ini | 2 +- tad/{alembic => migrations}/env.py | 3 +-- tad/{alembic => migrations}/script.py.mako | 0 tad/{alembic => migrations}/versions/99fb931b1324_.py | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) rename tad/alembic.ini => alembic.ini (99%) rename tad/{alembic => migrations}/env.py (99%) rename tad/{alembic => migrations}/script.py.mako (100%) rename tad/{alembic => migrations}/versions/99fb931b1324_.py (99%) diff --git a/tad/alembic.ini b/alembic.ini similarity index 99% rename from tad/alembic.ini rename to alembic.ini index c77a70cf..5f1146fb 100644 --- a/tad/alembic.ini +++ b/alembic.ini @@ -2,7 +2,7 @@ [alembic] # path to migration scripts -script_location = alembic +script_location = tad/migrations # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s # Uncomment the line below if you want the files to be prepended with date and time diff --git a/tad/alembic/env.py b/tad/migrations/env.py similarity index 99% rename from tad/alembic/env.py rename to tad/migrations/env.py index 5e3667ff..a5e1c4ad 100644 --- a/tad/alembic/env.py +++ b/tad/migrations/env.py @@ -1,9 +1,8 @@ import os from logging.config import fileConfig -from sqlalchemy import engine_from_config, pool - from alembic import context +from sqlalchemy import engine_from_config, pool config = context.config diff --git a/tad/alembic/script.py.mako b/tad/migrations/script.py.mako similarity index 100% rename from tad/alembic/script.py.mako rename to tad/migrations/script.py.mako diff --git a/tad/alembic/versions/99fb931b1324_.py b/tad/migrations/versions/99fb931b1324_.py similarity index 99% rename from tad/alembic/versions/99fb931b1324_.py rename to tad/migrations/versions/99fb931b1324_.py index 16719e38..edd267a8 100644 --- a/tad/alembic/versions/99fb931b1324_.py +++ b/tad/migrations/versions/99fb931b1324_.py @@ -9,7 +9,6 @@ from collections.abc import Sequence import sqlalchemy as sa - from alembic import op # revision identifiers, used by Alembic. From d0655f6418d6d0526b1d3c015f4f149ce3581fbb Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:26:00 +0000 Subject: [PATCH 29/55] Move dockerfile --- tad/Dockerfile => Dockerfile | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) rename tad/Dockerfile => Dockerfile (75%) diff --git a/tad/Dockerfile b/Dockerfile similarity index 75% rename from tad/Dockerfile rename to Dockerfile index 809ad599..56249b8a 100644 --- a/tad/Dockerfile +++ b/Dockerfile @@ -45,17 +45,26 @@ RUN ruff format --check RUN pyright FROM development AS test -RUN coverage run -m pytest ./tests +RUN coverage run -m pytest RUN coverage report FROM project-base AS production +RUN groupadd tad && \ + adduser --uid 100 --system --ingroup tad tad + +#todo(berry): create log folder so permissions can be set to root:root +# currenlt problem is that i could not get the logger to accept a path in the filerotate handler +RUN chown tad:tad /app/ + USER tad COPY --chown=root:root --chmod=755 ./tad /app/tad +COPY --chown=root:root --chmod=755 alembic.ini /app/alembic.ini +COPY --chown=root:root --chmod=755 .env /app/.env +COPY --chown=root:root --chmod=755 LICENSE /app/LICENSE ENV PYTHONPATH=/app/ WORKDIR /app/ -# change this to a usefull command CMD ["python", "-m", "granian", "--host", "0.0.0.0","--interface","asgi","tad.main:app" ] From 990e5259df304030cd411d135d6071b50b749fe3 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:26:30 +0000 Subject: [PATCH 30/55] Update sonar-porject and pyproject --- compose.yml | 2 +- pyproject.toml | 8 ++++---- sonar-project.properties | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compose.yml b/compose.yml index 5b49484d..a194a988 100644 --- a/compose.yml +++ b/compose.yml @@ -2,7 +2,7 @@ services: tad: build: context: . - dockerfile: tad/Dockerfile + dockerfile: Dockerfile image: ghcr.io/minbzk/tad:latest restart: unless-stopped depends_on: diff --git a/pyproject.toml b/pyproject.toml index 8bd1be0e..d3355483 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ build-backend = "poetry.core.masonry.api" [tool.ruff] line-length = 120 target-version = "py311" -src = ["tad"] +src = ["tad","tests"] [tool.ruff.format] line-ending = "lf" @@ -65,7 +65,7 @@ fixable = ["ALL"] task-tags = ["TODO"] [tool.ruff.lint.per-file-ignores] -"tad/tests/**.py" = ["S101"] +"tests/**.py" = ["S101"] [tool.ruff.lint.mccabe] max-complexity = 8 @@ -80,7 +80,7 @@ reportUnnecessaryIsInstance = false [tool.coverage.run] branch = true command_line = "-m pytest" -relative_files = true +relative_files = true # needed for sonarcloud code coverage [tool.coverage.report] fail_under = 95 @@ -92,7 +92,7 @@ title = "tad" [tool.pytest.ini_options] testpaths = [ - "tad/tests" + "tests" ] filterwarnings = [ "ignore::UserWarning" diff --git a/sonar-project.properties b/sonar-project.properties index d05f6bf2..4be98836 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -16,4 +16,4 @@ sonar.python.version=3.10,3.11,3.12 sonar.python.coverage.reportPaths=coverage.xml -sonar.coverage.exclusions="tad/alembic/*","tad/tests/*" +sonar.coverage.exclusions="tad/migration/*","tests/*" From 41a7956db51e3b6fbc761d0094fa2e3e93b38aaf Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:27:20 +0000 Subject: [PATCH 31/55] Add pythonpath to devcontainer --- .devcontainer/devcontainer.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index da0d1e50..1696c40a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,8 @@ "containerEnv": { "DEVCONTAINER": "1", "PYTHONASYNCIODEBUG": "1", - "POETRY_VIRTUALENVS_CREATE": "false" + "POETRY_VIRTUALENVS_CREATE": "false", + "PYTHONPATH": "/workspaces/tad/" }, "remoteUser": "root", "features": { @@ -16,7 +17,6 @@ "vscode": { "extensions": [ "ms-python.python", - "ms-python.vscode-pylance", "editorconfig.editorconfig", "streetsidesoftware.code-spell-checker", "redhat.vscode-yaml", @@ -42,7 +42,6 @@ "[python]": { "editor.defaultFormatter": "charliermarsh.ruff" }, - "python.languageServer": "Pylance", "python.analysis.typeCheckingMode": "strict", "markiscodecoverage.searchCriteria": "coverage.lcov" } From 571164fd9b2daae52393e75b709d5a987969e8f6 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:28:10 +0000 Subject: [PATCH 32/55] Add log to file tad.log --- .gitignore | 3 +++ tad/core/log.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++ tad/core/logger.py | 9 --------- 3 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 tad/core/log.py delete mode 100644 tad/core/logger.py diff --git a/.gitignore b/.gitignore index d8ab9c17..6f460810 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ __pypackages__/ # macos .DS_Store + +# tad tool +tad.log* diff --git a/tad/core/log.py b/tad/core/log.py new file mode 100644 index 00000000..b0a98a29 --- /dev/null +++ b/tad/core/log.py @@ -0,0 +1,48 @@ +import copy +import logging +import logging.config +from typing import Any + +from tad.core.types import LoggingLevelType + +LOGGING_SIZE = 10 * 1024 * 1024 +LOGGING_BACKUP_COUNT = 5 + +LOGGING_CONFIG = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "generic": { + "()": "logging.Formatter", + "style": "{", + "fmt": "{asctime}({levelname},{name}): {message}", + "datefmt": "[%Y-%m-%d %H:%M:%S %z]", + } + }, + "handlers": { + "console": {"formatter": "generic", "class": "logging.StreamHandler", "stream": "ext://sys.stdout"}, + "file": { + "formatter": "generic", + "()": "logging.handlers.RotatingFileHandler", + "filename": "tad.log", + "maxBytes": LOGGING_SIZE, + "backupCount": LOGGING_BACKUP_COUNT, + }, + }, + "loggers": { + "tad": {"handlers": ["console", "file"], "level": "DEBUG", "propagate": True}, + }, +} + + +def configure_logging(level: LoggingLevelType = "INFO", config: dict[str, Any] | None = None) -> None: + log_config = copy.deepcopy(LOGGING_CONFIG) + + if config: + log_config.update(config) + + logging.config.dictConfig(log_config) + + logger = logging.getLogger("tad") + + logger.setLevel(level) diff --git a/tad/core/logger.py b/tad/core/logger.py deleted file mode 100644 index 0efe6f43..00000000 --- a/tad/core/logger.py +++ /dev/null @@ -1,9 +0,0 @@ -import logging - -from tad.core.config import settings - - -def set_default_logging_setup(): - logging.basicConfig(level=settings.LOGGING_LEVEL, style="{", format="{asctime}({levelname},{name}): {message}") - # todo(berry): load from logging.config.dictConfig - # todo(berry): create RotatingFileHandler for logging to append to the default stream handler From 4edd1496141b1c9af4c6a2fd3fe0d557639f0bec Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:30:53 +0000 Subject: [PATCH 33/55] Move test to root --- tad/tests/core/test_logger.py | 17 ---- tad/tests/test_variables.py | 0 {tad/tests => tests}/__init__.py | 0 tests/api/routes/test_health.py | 19 +++++ tests/api/routes/test_root.py | 9 ++ {tad/tests => tests}/conftest.py | 5 +- {tad/tests => tests}/core/test_config.py | 18 ---- tests/core/test_exception_handlers.py | 34 ++++++++ {tad/tests => tests}/core/test_exceptions.py | 0 tests/core/test_log.py | 88 ++++++++++++++++++++ {tad/tests => tests}/test_main.py | 8 -- {tad/tests => tests}/utils/test_mask.py | 20 ++--- 12 files changed, 162 insertions(+), 56 deletions(-) delete mode 100644 tad/tests/core/test_logger.py delete mode 100644 tad/tests/test_variables.py rename {tad/tests => tests}/__init__.py (100%) create mode 100644 tests/api/routes/test_health.py create mode 100644 tests/api/routes/test_root.py rename {tad/tests => tests}/conftest.py (73%) rename {tad/tests => tests}/core/test_config.py (79%) create mode 100644 tests/core/test_exception_handlers.py rename {tad/tests => tests}/core/test_exceptions.py (100%) create mode 100644 tests/core/test_log.py rename {tad/tests => tests}/test_main.py (57%) rename {tad/tests => tests}/utils/test_mask.py (64%) diff --git a/tad/tests/core/test_logger.py b/tad/tests/core/test_logger.py deleted file mode 100644 index b510ac50..00000000 --- a/tad/tests/core/test_logger.py +++ /dev/null @@ -1,17 +0,0 @@ -import logging - -import pytest -from tad.core.logger import set_default_logging_setup - - -def test_set_default_logging_setup(caplog: pytest.LogCaptureFixture): - set_default_logging_setup() - - logger = logging.getLogger("tad.main") - - message = "This is a test log message" - logger.info(message) - logger.debug(message) - - assert len(caplog.records) == 1 - assert caplog.records[0].message == message diff --git a/tad/tests/test_variables.py b/tad/tests/test_variables.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tad/tests/__init__.py b/tests/__init__.py similarity index 100% rename from tad/tests/__init__.py rename to tests/__init__.py diff --git a/tests/api/routes/test_health.py b/tests/api/routes/test_health.py new file mode 100644 index 00000000..49edab92 --- /dev/null +++ b/tests/api/routes/test_health.py @@ -0,0 +1,19 @@ +from fastapi.testclient import TestClient + + +def test_health_ready(client: TestClient) -> None: + response = client.get( + "/health/ready", + ) + assert response.status_code == 200 + assert response.headers["content-type"] == "application/json" + assert response.json() == {"status": "ok"} + + +def test_health_live(client: TestClient) -> None: + response = client.get( + "/health/live", + ) + assert response.status_code == 200 + assert response.headers["content-type"] == "application/json" + assert response.json() == {"status": "ok"} diff --git a/tests/api/routes/test_root.py b/tests/api/routes/test_root.py new file mode 100644 index 00000000..80871570 --- /dev/null +++ b/tests/api/routes/test_root.py @@ -0,0 +1,9 @@ +from fastapi.testclient import TestClient + + +def test_get_root(client: TestClient) -> None: + response = client.get( + "/", + ) + assert response.status_code == 200 + assert response.headers["content-type"] == "text/html; charset=utf-8" diff --git a/tad/tests/conftest.py b/tests/conftest.py similarity index 73% rename from tad/tests/conftest.py rename to tests/conftest.py index 8bc5bfe8..ba26bced 100644 --- a/tad/tests/conftest.py +++ b/tests/conftest.py @@ -3,7 +3,6 @@ import pytest from fastapi.testclient import TestClient - from tad.main import app @@ -16,9 +15,9 @@ def client() -> Generator[TestClient, None, None]: @pytest.fixture(autouse=True) -def setup_basic_environmental_variables() -> Generator[None, None, None]: # noqa: PT004 +def setup_basic_environmental_variables(monkeypatch: pytest.MonkeyPatch) -> Generator[None, None, None]: # noqa: PT004 original_environ = dict(os.environ) - os.environ["APP_DATABASE_PASSWORD"] = "changethis" # noqa: S105 + monkeypatch.setenv("APP_DATABASE_PASSWORD", "changethis") yield os.environ.clear() os.environ.update(original_environ) diff --git a/tad/tests/core/test_config.py b/tests/core/test_config.py similarity index 79% rename from tad/tests/core/test_config.py rename to tests/core/test_config.py index d22b3da8..697f9124 100644 --- a/tad/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -1,7 +1,6 @@ import os import pytest -from pydantic import AnyUrl from tad.core.config import Settings from tad.core.exceptions import SettingsError @@ -12,7 +11,6 @@ def test_default_settings(): assert settings.DOMAIN == "localhost" assert settings.ENVIRONMENT == "local" assert settings.server_host == "http://localhost" - assert [] == settings.BACKEND_CORS_ORIGINS assert settings.VERSION == "0.1.0" assert settings.LOGGING_LEVEL == "INFO" assert settings.PROJECT_NAME == "TAD" @@ -30,7 +28,6 @@ def test_environment_settings(): os.environ["DOMAIN"] = "google.com" os.environ["ENVIRONMENT"] = "production" os.environ["PROJECT_NAME"] = "TAD2" - os.environ["BACKEND_CORS_ORIGINS"] = "http://google.com,https://google.com" os.environ["SECRET_KEY"] = "mysecret" # noqa: S105 os.environ["APP_DATABASE_SCHEME"] = "postgresql" os.environ["APP_DATABASE_USER"] = "tad2" @@ -42,7 +39,6 @@ def test_environment_settings(): assert settings.DOMAIN == "google.com" assert settings.ENVIRONMENT == "production" assert settings.server_host == "https://google.com" - assert [AnyUrl("http://google.com"), AnyUrl("https://google.com")] == settings.BACKEND_CORS_ORIGINS assert settings.VERSION == "0.1.0" assert settings.LOGGING_LEVEL == "INFO" assert settings.PROJECT_NAME == "TAD2" @@ -57,20 +53,6 @@ def test_environment_settings(): assert settings.SQLALCHEMY_DATABASE_URI == "postgresql://tad2:mypassword@db:5432/tad2" -def test_environment_settings_cors_list(): - os.environ["BACKEND_CORS_ORIGINS"] = "[http://google.com,https://google.com]" - - settings = Settings(_env_file="nonexisitingfile") # type: ignore - - assert settings.BACKEND_CORS_ORIGINS == "[http://google.com,https://google.com]" - - -def test_environment_settings_cors_error(): - os.environ["BACKEND_CORS_ORIGINS"] = "1234" - with pytest.raises(ValueError, match="1234"): - _settings = Settings(_env_file="nonexisitingfile") # type: ignore - - def test_environment_settings_production_sqlite_error(): os.environ["ENVIRONMENT"] = "production" os.environ["APP_DATABASE_SCHEME"] = "sqlite" diff --git a/tests/core/test_exception_handlers.py b/tests/core/test_exception_handlers.py new file mode 100644 index 00000000..91e2d0b2 --- /dev/null +++ b/tests/core/test_exception_handlers.py @@ -0,0 +1,34 @@ +import pytest +from fastapi import status +from fastapi.exceptions import HTTPException, RequestValidationError +from fastapi.testclient import TestClient +from tad.main import app + + +@pytest.fixture() +def _mock_error_500(): + """Add route to raise ValueError""" + + @app.get("/raise-http-exception") + async def raise_http_error(): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + + @app.get("/raise-request-validation-exception") + async def raise_request_error(): + raise RequestValidationError(errors=None) + + +@pytest.mark.usefixtures("_mock_error_500") +def test_http_exception_handler(client: TestClient): + response = client.get("/raise-http-exception") + + assert response.status_code == status.HTTP_404_NOT_FOUND + assert response.headers["content-type"] == "text/html; charset=utf-8" + + +@pytest.mark.usefixtures("_mock_error_500") +def test_request_validation_exception_handler(client: TestClient): + response = client.get("/raise-request-validation-exception") + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.headers["content-type"] == "text/html; charset=utf-8" diff --git a/tad/tests/core/test_exceptions.py b/tests/core/test_exceptions.py similarity index 100% rename from tad/tests/core/test_exceptions.py rename to tests/core/test_exceptions.py diff --git a/tests/core/test_log.py b/tests/core/test_log.py new file mode 100644 index 00000000..9cebc91a --- /dev/null +++ b/tests/core/test_log.py @@ -0,0 +1,88 @@ +import logging +import os + +import pytest +from tad.core.config import Settings +from tad.core.log import configure_logging + + +def test_module_logging_setup(caplog: pytest.LogCaptureFixture): + configure_logging() + + logger = logging.getLogger("tad") + + message = "This is a test log message" + logger.debug(message) # defaults to INFO level so debug is not printed + logger.info(message) + logger.warning(message) + logger.error(message) + logger.critical(message) + + assert len(caplog.records) == 4 + assert caplog.records[0].message == message + + +def test_root_logging_setup(caplog: pytest.LogCaptureFixture): + configure_logging() + + logger = logging.getLogger("") + + message = "This is a test log message" + logger.debug(message) + logger.info(message) + logger.warning(message) # defaults to warning + logger.error(message) + logger.critical(message) + + assert len(caplog.records) == 3 + assert caplog.records[0].message == message + + +def test_module_main_logging_setup(caplog: pytest.LogCaptureFixture): + configure_logging() + + logger = logging.getLogger("tad.main") + + message = "This is a test log message" + logger.debug(message) + logger.info(message) # should use module logger level + logger.warning(message) + logger.error(message) + logger.critical(message) + + assert len(caplog.records) == 4 + assert caplog.records[0].message == message + + +def test_module_main_logging_with_custom_logging_setup(caplog: pytest.LogCaptureFixture): + configure_logging(level="ERROR") + + logger = logging.getLogger("tad.main") + + message = "This is a test log message" + logger.debug(message) + logger.info(message) + logger.warning(message) + logger.error(message) + logger.critical(message) + + assert len(caplog.records) == 2 + + +def test_enviroment_setup(caplog: pytest.LogCaptureFixture): + os.environ["LOGGING_CONFIG"] = '{"formatters": { "generic": { "fmt": "{name}: {message}"}}}' + + settings = Settings() # type: ignore + + configure_logging(config=settings.LOGGING_CONFIG) + + logger = logging.getLogger("tad.main") + + message = "This is a test log message with other formatting" + logger.debug(message) + logger.info(message) + logger.warning(message) + logger.error(message) + logger.critical(message) + + assert len(caplog.records) == 4 diff --git a/tad/tests/test_main.py b/tests/test_main.py similarity index 57% rename from tad/tests/test_main.py rename to tests/test_main.py index c084568f..226f9e5f 100644 --- a/tad/tests/test_main.py +++ b/tests/test_main.py @@ -1,14 +1,6 @@ from fastapi.testclient import TestClient -def test_get_root(client: TestClient) -> None: - response = client.get( - "/", - ) - assert response.status_code == 200 - assert response.headers["content-type"] == "text/html; charset=utf-8" - - def test_get_non_exisiting(client: TestClient) -> None: response = client.get( "/pathdoesnotexist/", diff --git a/tad/tests/utils/test_mask.py b/tests/utils/test_mask.py similarity index 64% rename from tad/tests/utils/test_mask.py rename to tests/utils/test_mask.py index f72c3f22..36258027 100644 --- a/tad/tests/utils/test_mask.py +++ b/tests/utils/test_mask.py @@ -1,13 +1,13 @@ -from tad.utils.mask import DataMasker +from tad.utils.mask import Mask def test_mask_dict(): # given data: dict[str | int, str] = {1: "value1", "MY_PASSWORD": "value2", "key3": "value3"} - data_masker = DataMasker() + mask = Mask() # when - masked_data = data_masker.mask_data(data) + masked_data = mask.secrets(data) # then assert masked_data == {1: "value1", "MY_PASSWORD": "***MASKED***", "key3": "value3"} @@ -16,10 +16,10 @@ def test_mask_dict(): def test_mask_set(): # given data: set[str | int] = {"value1", "password", "value3", 4} - data_masker = DataMasker() + mask = Mask() # when - masked_data = data_masker.mask_data(data) + masked_data = mask.secrets(data) # then assert masked_data == {"value1", "***MASKED***", "value3", 4} @@ -28,10 +28,10 @@ def test_mask_set(): def test_mask_list(): # given data: list[str | int] = [1, "mysecret", "notimportant"] - data_masker = DataMasker(mask_value="******") + mask = Mask(mask_value="******") # when - masked_data = data_masker.mask_data(data) + masked_data = mask.secrets(data) # then assert masked_data == [1, "******", "notimportant"] @@ -41,11 +41,11 @@ def test_mask_string(): # given data: str = "my_token" data_nosecret: str = "my_notimportant" - data_masker = DataMasker(mask_keywords=["token"]) + mask = Mask(mask_keywords=["token"]) # when - masked_data = data_masker.mask_data(data) - masked_data_nosecret = data_masker.mask_data(data_nosecret) + masked_data = mask.secrets(data) + masked_data_nosecret = mask.secrets(data_nosecret) # then assert masked_data == "***MASKED***" From 4d97273063c0df27faa2f010b0b23953f7441af2 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:31:09 +0000 Subject: [PATCH 34/55] Start debugger from root --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index debd23c4..84d3ec13 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,8 +7,8 @@ "request": "launch", "module": "granian", "justMyCode": true, - "args": ["--interface","asgi","main:app", "--reload"], - "cwd": "${workspaceFolder}/tad/", + "args": ["--interface","asgi","tad.main:app", "--reload"], + "cwd": "${workspaceFolder}/", "env": { "PYTHONPATH": "${workspaceFolder}" } From 334069cac2eb974f4118e03b056abedbdb78bf3f Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:31:30 +0000 Subject: [PATCH 35/55] Add routes --- tad/api/__init__.py | 0 tad/api/main.py | 7 +++++++ tad/api/routes/__init__.py | 0 tad/api/routes/health.py | 15 +++++++++++++++ tad/api/routes/root.py | 9 +++++++++ 5 files changed, 31 insertions(+) create mode 100644 tad/api/__init__.py create mode 100644 tad/api/main.py create mode 100644 tad/api/routes/__init__.py create mode 100644 tad/api/routes/health.py create mode 100644 tad/api/routes/root.py diff --git a/tad/api/__init__.py b/tad/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tad/api/main.py b/tad/api/main.py new file mode 100644 index 00000000..6f604c7e --- /dev/null +++ b/tad/api/main.py @@ -0,0 +1,7 @@ +from fastapi import APIRouter + +from tad.api.routes import health, root + +api_router = APIRouter() +api_router.include_router(root.router) +api_router.include_router(health.router, prefix="/health", tags=["health"]) diff --git a/tad/api/routes/__init__.py b/tad/api/routes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tad/api/routes/health.py b/tad/api/routes/health.py new file mode 100644 index 00000000..ee471e6b --- /dev/null +++ b/tad/api/routes/health.py @@ -0,0 +1,15 @@ +from fastapi import APIRouter +from fastapi.responses import JSONResponse + +router = APIRouter() + + +@router.get("/live", response_class=JSONResponse) +async def liveness(): + return {"status": "ok"} + + +@router.get("/ready", response_class=JSONResponse) +async def readiness(): + # todo(berry): Add database connection check + return {"status": "ok"} diff --git a/tad/api/routes/root.py b/tad/api/routes/root.py new file mode 100644 index 00000000..2aa20e12 --- /dev/null +++ b/tad/api/routes/root.py @@ -0,0 +1,9 @@ +from fastapi import APIRouter +from fastapi.responses import HTMLResponse + +router = APIRouter() + + +@router.get("/") +async def base(): + return HTMLResponse(content="message - Hello World 2 sdfasdfsda") From 39d43f713ef1d5ab9be1062abe49a211dcb309b5 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:31:58 +0000 Subject: [PATCH 36/55] Add logging variable to app --- tad/core/config.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tad/core/config.py b/tad/core/config.py index 3fe570bc..2ab11f92 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -1,10 +1,8 @@ import secrets import warnings -from typing import Annotated, Any, Literal, TypeVar +from typing import Any, TypeVar from pydantic import ( - AnyUrl, - BeforeValidator, computed_field, model_validator, ) @@ -12,26 +10,19 @@ from pydantic_settings import BaseSettings, SettingsConfigDict from tad.core.exceptions import SettingsError +from tad.core.types import DatabaseSchemaType, EnvironmentType, LoggingLevelType # Self type is not available in Python 3.10 so create our own with TypeVar SelfSettings = TypeVar("SelfSettings", bound="Settings") -def parse_cors(v: Any) -> list[str] | str: - if isinstance(v, str) and not v.startswith("["): - return [i.strip() for i in v.split(",")] - elif isinstance(v, list | str): - return v - raise ValueError(v) - - class Settings(BaseSettings): # todo(berry): investigate yaml, toml or json file support for SettingsConfigDict model_config = SettingsConfigDict(env_file=".env", env_ignore_empty=True, extra="ignore") SECRET_KEY: str = secrets.token_urlsafe(32) DOMAIN: str = "localhost" - ENVIRONMENT: Literal["local", "staging", "production"] = "local" + ENVIRONMENT: EnvironmentType = "local" @computed_field # type: ignore[misc] @property @@ -40,16 +31,18 @@ def server_host(self) -> str: return f"http://{self.DOMAIN}" return f"https://{self.DOMAIN}" - BACKEND_CORS_ORIGINS: Annotated[list[AnyUrl] | str, BeforeValidator(parse_cors)] = [] VERSION: str = "0.1.0" - LOGGING_LEVEL: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO" + LOGGING_LEVEL: LoggingLevelType = "INFO" + LOGGING_CONFIG: dict[str, Any] | None = None PROJECT_NAME: str = "TAD" PROJECT_DESCRIPTION: str = "Transparency of Algorithmic Decision making" + STATIC_DIR: str = "tad/static" + # todo(berry): create submodel for database settings - APP_DATABASE_SCHEME: Literal["sqlite", "postgresql", "mysql", "oracle"] = "sqlite" + APP_DATABASE_SCHEME: DatabaseSchemaType = "sqlite" APP_DATABASE_SERVER: str = "db" APP_DATABASE_PORT: int = 5432 APP_DATABASE_USER: str = "tad" From f9204fdacb41944acb9cac20fd1a208ead1b3477 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 13:58:19 +0000 Subject: [PATCH 37/55] Make models a folder --- BUILD.md | 16 ++++++++++++++++ alembic.ini | 8 ++++---- tad/core/types.py | 5 +++++ tad/migrations/env.py | 9 +++++---- ... => 17f5b05e9ec3_added_hero_test_table.py} | 19 +++++++++++-------- tad/models/__init__.py | 0 tad/{models.py => models/hero.py} | 0 tad/utils/mask.py | 16 ++++++++-------- 8 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 tad/core/types.py rename tad/migrations/versions/{99fb931b1324_.py => 17f5b05e9ec3_added_hero_test_table.py} (50%) create mode 100644 tad/models/__init__.py rename tad/{models.py => models/hero.py} (100%) diff --git a/BUILD.md b/BUILD.md index 0c3fb396..17713073 100644 --- a/BUILD.md +++ b/BUILD.md @@ -23,6 +23,22 @@ When poetry is done installing all dependencies you can start using the tool. poetry run python -m uvicorn tad.main:app ``` +## Database + +We support most SQL database types. You can use the variable `APP_DATABASE_SCHEME` to change the database. The default scheme is sqlite. + +If you change the `models` at tad/models of the application you can generate a new migration file +```shell +alembic revision --autogenerate -m "a message" +``` + +Please make sure you check the auto generated file in tad/migrations/ + +to upgrade to the latest version of the database schema use +```shell +alembic upgrade head +``` + ## Building TAD with Containers Containers allows to package software, make it portable, and isolated. Before you can run a container you first need a container runtime. There are several available, but al lot of users use [docker desktop](https://www.docker.com/products/docker-desktop/). diff --git a/alembic.ini b/alembic.ini index 5f1146fb..bfc48614 100644 --- a/alembic.ini +++ b/alembic.ini @@ -75,10 +75,10 @@ script_location = tad/migrations # black.options = -l 79 REVISION_SCRIPT_FILENAME # lint with attempts to fix using "ruff" - use the exec runner, execute a binary -# hooks = ruff -# ruff.type = exec -# ruff.executable = %(here)s/.venv/bin/ruff -# ruff.options = --fix REVISION_SCRIPT_FILENAME +hooks = ruff +ruff.type = exec +ruff.executable = ruff +ruff.options = check --fix REVISION_SCRIPT_FILENAME # Logging configuration [loggers] diff --git a/tad/core/types.py b/tad/core/types.py new file mode 100644 index 00000000..4848e7f3 --- /dev/null +++ b/tad/core/types.py @@ -0,0 +1,5 @@ +from typing import Literal + +EnvironmentType = Literal["local", "staging", "production"] +LoggingLevelType = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] +DatabaseSchemaType = Literal["sqlite", "postgresql", "mysql", "oracle"] diff --git a/tad/migrations/env.py b/tad/migrations/env.py index a5e1c4ad..51cd0f77 100644 --- a/tad/migrations/env.py +++ b/tad/migrations/env.py @@ -9,17 +9,18 @@ if config.config_file_name is not None: fileConfig(config.config_file_name) -from tad.models import SQLModel # noqa +# todo(berry): automaticly import all models +from tad.models.hero import Hero # noqa -target_metadata = SQLModel.metadata +target_metadata = [Hero.metadata] def get_url(): scheme = os.getenv("SQLALCHEMY_SCHEME", "sqlite") if scheme == "sqlite": - file = os.getenv("SQLITE_FILE", "./database") - return f"{scheme}://{file}" + file = os.getenv("SQLITE_FILE", "/database") + return f"{scheme}:///{file}" user = os.getenv("APP_DATABASE_USER", "postgres") password = os.getenv("APP_DATABASE_PASSWORD", "") diff --git a/tad/migrations/versions/99fb931b1324_.py b/tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py similarity index 50% rename from tad/migrations/versions/99fb931b1324_.py rename to tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py index edd267a8..b3f13cba 100644 --- a/tad/migrations/versions/99fb931b1324_.py +++ b/tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py @@ -1,33 +1,36 @@ -"""empty message +"""Added hero test table -Revision ID: 99fb931b1324 +Revision ID: 17f5b05e9ec3 Revises: -Create Date: 2024-05-02 12:04:56.694709 +Create Date: 2024-05-10 13:57:16.989570 """ from collections.abc import Sequence import sqlalchemy as sa +import sqlmodel.sql.sqltypes from alembic import op # revision identifiers, used by Alembic. -revision: str = "99fb931b1324" +revision: str = "17f5b05e9ec3" down_revision: str | None = None branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### op.create_table( "hero", sa.Column("id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(), nullable=False), - sa.Column("secret_name", sa.String(), nullable=False), - sa.Column("age", sa.Integer(), nullable=True), + sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.PrimaryKeyConstraint("id"), ) + # ### end Alembic commands ### def downgrade() -> None: - pass + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("hero") + # ### end Alembic commands ### diff --git a/tad/models/__init__.py b/tad/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tad/models.py b/tad/models/hero.py similarity index 100% rename from tad/models.py rename to tad/models/hero.py diff --git a/tad/utils/mask.py b/tad/utils/mask.py index f4613e27..73fc6c8b 100644 --- a/tad/utils/mask.py +++ b/tad/utils/mask.py @@ -1,21 +1,21 @@ from typing import Any -class DataMasker: +class Mask: def __init__(self, mask_value: str = "***MASKED***", mask_keywords: list[str] | None = None): if mask_keywords is None: mask_keywords = [] self.mask_value = mask_value # default keywords to mask - self.keywords: list[str] = ["password", "secret", "database_uri"] + self.keywords: list[str] = ["password", "secret"] self.keywords.extend(mask_keywords or []) - def mask_data( # noqa: C901 + def secrets( # noqa: C901ß self, data: str | list[Any] | dict[Any, Any] | set[Any] ) -> str | list[Any] | dict[Any, Any] | set[Any]: if isinstance(data, dict): - masked_dict: dict[str | int, str | int] = {} + masked_dict: dict[Any, Any] = {} for key, value in data.items(): if isinstance(key, str): for keyword in self.keywords: @@ -29,8 +29,8 @@ def mask_data( # noqa: C901 return masked_dict elif isinstance(data, list): - masked_list: list[str | int] = [] - item: str | int + masked_list: list[Any] = [] + item: Any for item in data: if isinstance(item, str): for keyword in self.keywords: @@ -44,9 +44,9 @@ def mask_data( # noqa: C901 return masked_list elif isinstance(data, set): - masked_set: set[str | int] = set() + masked_set: set[Any] = set() - item: str | int + item: Any for item in data: if isinstance(item, str): for keyword in self.keywords: From 6f8addf91ea4615f34f259a07410081006c8c5d3 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 14:01:01 +0000 Subject: [PATCH 38/55] Add ruff formatter to alembic --- alembic.ini | 6 +++++- ..._test_table.py => a1d47bd833f8_added_hero_test_table.py} | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) rename tad/migrations/versions/{17f5b05e9ec3_added_hero_test_table.py => a1d47bd833f8_added_hero_test_table.py} (89%) diff --git a/alembic.ini b/alembic.ini index bfc48614..2e8e170c 100644 --- a/alembic.ini +++ b/alembic.ini @@ -75,11 +75,15 @@ script_location = tad/migrations # black.options = -l 79 REVISION_SCRIPT_FILENAME # lint with attempts to fix using "ruff" - use the exec runner, execute a binary -hooks = ruff +hooks = ruff, ruff_format ruff.type = exec ruff.executable = ruff ruff.options = check --fix REVISION_SCRIPT_FILENAME +ruff_format.type = exec +ruff_format.executable = ruff +ruff_format.options = format REVISION_SCRIPT_FILENAME + # Logging configuration [loggers] keys = root,sqlalchemy,alembic diff --git a/tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py b/tad/migrations/versions/a1d47bd833f8_added_hero_test_table.py similarity index 89% rename from tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py rename to tad/migrations/versions/a1d47bd833f8_added_hero_test_table.py index b3f13cba..d945dc80 100644 --- a/tad/migrations/versions/17f5b05e9ec3_added_hero_test_table.py +++ b/tad/migrations/versions/a1d47bd833f8_added_hero_test_table.py @@ -1,8 +1,8 @@ """Added hero test table -Revision ID: 17f5b05e9ec3 +Revision ID: a1d47bd833f8 Revises: -Create Date: 2024-05-10 13:57:16.989570 +Create Date: 2024-05-10 14:00:32.948932 """ @@ -13,7 +13,7 @@ from alembic import op # revision identifiers, used by Alembic. -revision: str = "17f5b05e9ec3" +revision: str = "a1d47bd833f8" down_revision: str | None = None branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None From 4e5e061481acd49045d873b9e81b8cc2376be810 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Fri, 10 May 2024 14:02:50 +0000 Subject: [PATCH 39/55] Add __init__ --- tad/middleware/__init__.py | 0 tad/utils/__init__.py | 0 tests/api/__init__.py | 0 tests/api/routes/__init__.py | 0 tests/core/__init__.py | 0 tests/utils/__init__.py | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tad/middleware/__init__.py create mode 100644 tad/utils/__init__.py create mode 100644 tests/api/__init__.py create mode 100644 tests/api/routes/__init__.py create mode 100644 tests/core/__init__.py create mode 100644 tests/utils/__init__.py diff --git a/tad/middleware/__init__.py b/tad/middleware/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tad/utils/__init__.py b/tad/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/api/__init__.py b/tests/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/api/routes/__init__.py b/tests/api/routes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/core/__init__.py b/tests/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py new file mode 100644 index 00000000..e69de29b From f8d13fe6ed61b54f14fc61ac79d388fe75a1a6e4 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Mon, 13 May 2024 13:04:26 +0000 Subject: [PATCH 40/55] Added a test .env file --- .env.test | 24 ++++++++++++++++++++++++ .vscode/extensions.json | 3 ++- .vscode/launch.json | 3 ++- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 .env.test diff --git a/.env.test b/.env.test new file mode 100644 index 00000000..4b36cdf8 --- /dev/null +++ b/.env.test @@ -0,0 +1,24 @@ +# Domain +DOMAIN=localhost + +# Environment: local, staging, production +ENVIRONMENT=local +PROJECT_NAME="TAD" + +# TAD backend +BACKEND_CORS_ORIGINS="http://localhost,https://localhost,http://127.0.0.1,https://127.0.0.1" +SECRET_KEY=changethis +APP_DATABASE_SCHEME="sqlite" +APP_DATABASE_USER=tad +APP_DATABASE_DB=tad +APP_DATABASE_PASSWORD=changethis + +# Postgres database +POSTGRES_SERVER=db +POSTGRES_PORT=5432 +POSTGRES_DB=postgres +POSTGRES_USER=postgres +POSTGRES_PASSWORD=changethis + +# Database viewer +PGADMIN_DEFAULT_PASSWORD=changethis diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 15f9cc4d..16c15f27 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -8,6 +8,7 @@ "charliermarsh.ruff", "github.vscode-github-actions", "mhutchie.git-graph", - "markis.code-coverage" + "markis.code-coverage", + "qwtel.sqlite-viewer" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 84d3ec13..c12e03b6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,8 @@ "cwd": "${workspaceFolder}/", "env": { "PYTHONPATH": "${workspaceFolder}" - } + }, + "envFile": "${workspaceFolder}/.env.test" }, { "name": "Project: tests", From 0dd830c3e139966c0ddc0895301f68350a1ce0e3 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Mon, 13 May 2024 13:05:09 +0000 Subject: [PATCH 41/55] Make models dynamicly imported for alembic --- tad/migrations/env.py | 13 ++++++------- ...hero_test_table.py => 006c480a1920_a_message.py} | 8 ++++---- 2 files changed, 10 insertions(+), 11 deletions(-) rename tad/migrations/versions/{a1d47bd833f8_added_hero_test_table.py => 006c480a1920_a_message.py} (86%) diff --git a/tad/migrations/env.py b/tad/migrations/env.py index 51cd0f77..d5bc0598 100644 --- a/tad/migrations/env.py +++ b/tad/migrations/env.py @@ -3,26 +3,25 @@ from alembic import context from sqlalchemy import engine_from_config, pool +from sqlmodel import SQLModel +from tad.models import * # noqa config = context.config if config.config_file_name is not None: fileConfig(config.config_file_name) -# todo(berry): automaticly import all models -from tad.models.hero import Hero # noqa - -target_metadata = [Hero.metadata] +target_metadata = SQLModel.metadata def get_url(): - scheme = os.getenv("SQLALCHEMY_SCHEME", "sqlite") + scheme = os.getenv("APP_DATABASE_SCHEME", "sqlite") if scheme == "sqlite": - file = os.getenv("SQLITE_FILE", "/database") + file = os.getenv("APP_DATABASE__FILE", "/database") return f"{scheme}:///{file}" - user = os.getenv("APP_DATABASE_USER", "postgres") + user = os.getenv("APP_DATABASE_USER", "tad") password = os.getenv("APP_DATABASE_PASSWORD", "") server = os.getenv("APP_DATABASE_SERVER", "db") port = os.getenv("APP_DATABASE_PORT", "5432") diff --git a/tad/migrations/versions/a1d47bd833f8_added_hero_test_table.py b/tad/migrations/versions/006c480a1920_a_message.py similarity index 86% rename from tad/migrations/versions/a1d47bd833f8_added_hero_test_table.py rename to tad/migrations/versions/006c480a1920_a_message.py index d945dc80..fa83e759 100644 --- a/tad/migrations/versions/a1d47bd833f8_added_hero_test_table.py +++ b/tad/migrations/versions/006c480a1920_a_message.py @@ -1,8 +1,8 @@ -"""Added hero test table +"""a message -Revision ID: a1d47bd833f8 +Revision ID: 006c480a1920 Revises: -Create Date: 2024-05-10 14:00:32.948932 +Create Date: 2024-05-13 12:36:32.647256 """ @@ -13,7 +13,7 @@ from alembic import op # revision identifiers, used by Alembic. -revision: str = "a1d47bd833f8" +revision: str = "006c480a1920" down_revision: str | None = None branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None From 9f1b180c40b08b5a4ce3024932161ae4917d4e93 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Mon, 13 May 2024 13:05:30 +0000 Subject: [PATCH 42/55] Add SQLite plugin for vscode --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1696c40a..27bc2fe2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -23,7 +23,8 @@ "charliermarsh.ruff", "github.vscode-github-actions", "mhutchie.git-graph", - "markis.code-coverage" + "markis.code-coverage", + "qwtel.sqlite-viewer" ], "settings": { "editor.formatOnPaste": false, From 90fbd94f565e05aa7c5ecf7b85f265d9a4153b63 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Mon, 13 May 2024 13:07:16 +0000 Subject: [PATCH 43/55] Monkeypatch env in tests --- .gitignore | 1 + compose.yml | 8 +- poetry.lock | 224 +++++++++++--------------- pyproject.toml | 10 +- tad/core/config.py | 40 +++-- tad/core/db.py | 9 +- tad/main.py | 2 + tad/models/__init__.py | 3 + tests/conftest.py | 27 ++-- tests/core/test_config.py | 44 +++-- tests/core/test_db.py | 15 ++ tests/core/test_exception_handlers.py | 16 +- 12 files changed, 201 insertions(+), 198 deletions(-) create mode 100644 tests/core/test_db.py diff --git a/.gitignore b/.gitignore index 6f460810..a0cbd221 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ __pypackages__/ # tad tool tad.log* +database.sqlite3 diff --git a/compose.yml b/compose.yml index a194a988..324d3887 100644 --- a/compose.yml +++ b/compose.yml @@ -9,7 +9,8 @@ services: db: condition: service_healthy env_file: - - .env + - path: .env + required: true environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Variable not set} ports: @@ -22,7 +23,8 @@ services: - app-db-data:/var/lib/postgresql/data/pgdata - ./database/:/docker-entrypoint-initdb.d/:cached env_file: - - .env + - path: .env + required: true environment: - PGDATA=/var/lib/postgresql/data/pgdata - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Variable not set} @@ -38,7 +40,7 @@ services: ports: - 8080:8080 environment: - - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL:-berry.hartog@minbzk.nl} + - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL:-tad@minbzk.nl} - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD:?Variable not set} - PGADMIN_LISTEN_PORT=${PGADMIN_LISTEN_PORT:-8080} depends_on: diff --git a/poetry.lock b/poetry.lock index 6b6eb6e3..f35389eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alembic" @@ -1096,101 +1096,84 @@ wcwidth = "*" tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"] [[package]] -name = "psycopg" -version = "3.1.18" -description = "PostgreSQL database adapter for Python" +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg-3.1.18-py3-none-any.whl", hash = "sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e"}, - {file = "psycopg-3.1.18.tar.gz", hash = "sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b"}, -] - -[package.dependencies] -psycopg-binary = {version = "3.1.18", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} -typing-extensions = ">=4.1" -tzdata = {version = "*", markers = "sys_platform == \"win32\""} - -[package.extras] -binary = ["psycopg-binary (==3.1.18)"] -c = ["psycopg-c (==3.1.18)"] -dev = ["black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] -docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] -pool = ["psycopg-pool"] -test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] - -[[package]] -name = "psycopg-binary" -version = "3.1.18" -description = "PostgreSQL database adapter for Python -- C optimisation distribution" -optional = false -python-versions = ">=3.7" -files = [ - {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c323103dfa663b88204cf5f028e83c77d7a715f9b6f51d2bbc8184b99ddd90a"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:887f8d856c91510148be942c7acd702ccf761a05f59f8abc123c22ab77b5a16c"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d322ba72cde4ca2eefc2196dad9ad7e52451acd2f04e3688d590290625d0c970"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:489aa4fe5a0b653b68341e9e44af247dedbbc655326854aa34c163ef1bcb3143"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ff0948457bfa8c0d35c46e3a75193906d1c275538877ba65907fd67aa059ad"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15e3653c82384b043d820fc637199b5c6a36b37fa4a4943e0652785bb2bad5d"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f8ff3bc08b43f36fdc24fedb86d42749298a458c4724fb588c4d76823ac39f54"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1729d0e3dfe2546d823841eb7a3d003144189d6f5e138ee63e5227f8b75276a5"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:13bcd3742112446037d15e360b27a03af4b5afcf767f5ee374ef8f5dd7571b31"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:320047e3d3554b857e16c2b6b615a85e0db6a02426f4d203a4594a2f125dfe57"}, - {file = "psycopg_binary-3.1.18-cp310-cp310-win_amd64.whl", hash = "sha256:888a72c2aca4316ca6d4a619291b805677bae99bba2f6e31a3c18424a48c7e4d"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e4de16a637ec190cbee82e0c2dc4860fed17a23a35f7a1e6dc479a5c6876722"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6432047b8b24ef97e3fbee1d1593a0faaa9544c7a41a2c67d1f10e7621374c83"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d684227ef8212e27da5f2aff9d4d303cc30b27ac1702d4f6881935549486dd5"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67284e2e450dc7a9e4d76e78c0bd357dc946334a3d410defaeb2635607f632cd"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c9b6bd7fb5c6638cb32469674707649b526acfe786ba6d5a78ca4293d87bae4"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7121acc783c4e86d2d320a7fb803460fab158a7f0a04c5e8c5d49065118c1e73"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e28ff8f3de7b56588c2a398dc135fd9f157d12c612bd3daa7e6ba9872337f6f5"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c84a0174109f329eeda169004c7b7ca2e884a6305acab4a39600be67f915ed38"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:531381f6647fc267383dca88dbe8a70d0feff433a8e3d0c4939201fea7ae1b82"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b293e01057e63c3ac0002aa132a1071ce0fdb13b9ee2b6b45d3abdb3525c597d"}, - {file = "psycopg_binary-3.1.18-cp311-cp311-win_amd64.whl", hash = "sha256:780a90bcb69bf27a8b08bc35b958e974cb6ea7a04cdec69e737f66378a344d68"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:87dd9154b757a5fbf6d590f6f6ea75f4ad7b764a813ae04b1d91a70713f414a1"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f876ebbf92db70125f6375f91ab4bc6b27648aa68f90d661b1fc5affb4c9731c"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d2f0cb45e4574f8b2fe7c6d0a0e2eb58903a4fd1fbaf60954fba82d595ab7"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd27f713f2e5ef3fd6796e66c1a5203a27a30ecb847be27a78e1df8a9a5ae68c"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c38a4796abf7380f83b1653c2711cb2449dd0b2e5aca1caa75447d6fa5179c69"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2f7f95746efd1be2dc240248cc157f4315db3fd09fef2adfcc2a76e24aa5741"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4085f56a8d4fc8b455e8f44380705c7795be5317419aa5f8214f315e4205d804"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2e2484ae835dedc80cdc7f1b1a939377dc967fed862262cfd097aa9f50cade46"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c2b039ae0c45eee4cd85300ef802c0f97d0afc78350946a5d0ec77dd2d7e834"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f54978c4b646dec77fefd8485fa82ec1a87807f334004372af1aaa6de9539a5"}, - {file = "psycopg_binary-3.1.18-cp312-cp312-win_amd64.whl", hash = "sha256:9ffcbbd389e486d3fd83d30107bbf8b27845a295051ccabde240f235d04ed921"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c76659ae29a84f2c14f56aad305dd00eb685bd88f8c0a3281a9a4bc6bd7d2aa7"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7afcd6f1d55992f26d9ff7b0bd4ee6b475eb43aa3f054d67d32e09f18b0065"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:639dd78ac09b144b0119076783cb64e1128cc8612243e9701d1503c816750b2e"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1cf59e0bb12e031a48bb628aae32df3d0c98fd6c759cb89f464b1047f0ca9c8"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e262398e5d51563093edf30612cd1e20fedd932ad0994697d7781ca4880cdc3d"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:59701118c7d8842e451f1e562d08e8708b3f5d14974eefbce9374badd723c4ae"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dea4a59da7850192fdead9da888e6b96166e90608cf39e17b503f45826b16f84"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4575da95fc441244a0e2ebaf33a2b2f74164603341d2046b5cde0a9aa86aa7e2"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:812726266ab96de681f2c7dbd6b734d327f493a78357fcc16b2ac86ff4f4e080"}, - {file = "psycopg_binary-3.1.18-cp37-cp37m-win_amd64.whl", hash = "sha256:3e7ce4d988112ca6c75765c7f24c83bdc476a6a5ce00878df6c140ca32c3e16d"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:02bd4da45d5ee9941432e2e9bf36fa71a3ac21c6536fe7366d1bd3dd70d6b1e7"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:39242546383f6b97032de7af30edb483d237a0616f6050512eee7b218a2aa8ee"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d46ae44d66bf6058a812467f6ae84e4e157dee281bfb1cfaeca07dee07452e85"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad35ac7fd989184bf4d38a87decfb5a262b419e8ba8dcaeec97848817412c64a"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:247474af262bdd5559ee6e669926c4f23e9cf53dae2d34c4d991723c72196404"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ebecbf2406cd6875bdd2453e31067d1bd8efe96705a9489ef37e93b50dc6f09"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1859aeb2133f5ecdd9cbcee155f5e38699afc06a365f903b1512c765fd8d457e"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:da917f6df8c6b2002043193cb0d74cc173b3af7eb5800ad69c4e1fbac2a71c30"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9e24e7b6a68a51cc3b162d0339ae4e1263b253e887987d5c759652f5692b5efe"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e252d66276c992319ed6cd69a3ffa17538943954075051e992143ccbf6dc3d3e"}, - {file = "psycopg_binary-3.1.18-cp38-cp38-win_amd64.whl", hash = "sha256:5d6e860edf877d4413e4a807e837d55e3a7c7df701e9d6943c06e460fa6c058f"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eea5f14933177ffe5c40b200f04f814258cc14b14a71024ad109f308e8bad414"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:824a1bfd0db96cc6bef2d1e52d9e0963f5bf653dd5bc3ab519a38f5e6f21c299"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a87e9eeb80ce8ec8c2783f29bce9a50bbcd2e2342a340f159c3326bf4697afa1"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91074f78a9f890af5f2c786691575b6b93a4967ad6b8c5a90101f7b8c1a91d9c"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e05f6825f8db4428782135e6986fec79b139210398f3710ed4aa6ef41473c008"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f68ac2364a50d4cf9bb803b4341e83678668f1881a253e1224574921c69868c"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7ac1785d67241d5074f8086705fa68e046becea27964267ab3abd392481d7773"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cd2a9f7f0d4dacc5b9ce7f0e767ae6cc64153264151f50698898c42cabffec0c"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:3e4b0bb91da6f2238dbd4fbb4afc40dfb4f045bb611b92fce4d381b26413c686"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:74e498586b72fb819ca8ea82107747d0cb6e00ae685ea6d1ab3f929318a8ce2d"}, - {file = "psycopg_binary-3.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:d4422af5232699f14b7266a754da49dc9bcd45eba244cf3812307934cd5d6679"}, + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, ] [[package]] @@ -1352,13 +1335,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyright" -version = "1.1.361" +version = "1.1.362" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.361-py3-none-any.whl", hash = "sha256:c50fc94ce92b5c958cfccbbe34142e7411d474da43d6c14a958667e35b9df7ea"}, - {file = "pyright-1.1.361.tar.gz", hash = "sha256:1d67933315666b05d230c85ea8fb97aaa2056e4092a13df87b7765bb9e8f1a8d"}, + {file = "pyright-1.1.362-py3-none-any.whl", hash = "sha256:969957cff45154d8a45a4ab1dae5bdc8223d8bd3c64654fa608ab3194dfff319"}, + {file = "pyright-1.1.362.tar.gz", hash = "sha256:6a477e448d4a07a6a0eab58b2a15a1bbed031eb3169fa809edee79cca168d83a"}, ] [package.dependencies] @@ -1628,28 +1611,28 @@ files = [ [[package]] name = "ruff" -version = "0.4.3" +version = "0.4.4" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.4.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b70800c290f14ae6fcbb41bbe201cf62dfca024d124a1f373e76371a007454ce"}, - {file = "ruff-0.4.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:08a0d6a22918ab2552ace96adeaca308833873a4d7d1d587bb1d37bae8728eb3"}, - {file = "ruff-0.4.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba1f14df3c758dd7de5b55fbae7e1c8af238597961e5fb628f3de446c3c40c5"}, - {file = "ruff-0.4.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:819fb06d535cc76dfddbfe8d3068ff602ddeb40e3eacbc90e0d1272bb8d97113"}, - {file = "ruff-0.4.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bfc9e955e6dc6359eb6f82ea150c4f4e82b660e5b58d9a20a0e42ec3bb6342b"}, - {file = "ruff-0.4.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:510a67d232d2ebe983fddea324dbf9d69b71c4d2dfeb8a862f4a127536dd4cfb"}, - {file = "ruff-0.4.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9ff11cd9a092ee7680a56d21f302bdda14327772cd870d806610a3503d001f"}, - {file = "ruff-0.4.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29efff25bf9ee685c2c8390563a5b5c006a3fee5230d28ea39f4f75f9d0b6f2f"}, - {file = "ruff-0.4.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b00e0bcccf0fc8d7186ed21e311dffd19761cb632241a6e4fe4477cc80ef6e"}, - {file = "ruff-0.4.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:262f5635e2c74d80b7507fbc2fac28fe0d4fef26373bbc62039526f7722bca1b"}, - {file = "ruff-0.4.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7363691198719c26459e08cc17c6a3dac6f592e9ea3d2fa772f4e561b5fe82a3"}, - {file = "ruff-0.4.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eeb039f8428fcb6725bb63cbae92ad67b0559e68b5d80f840f11914afd8ddf7f"}, - {file = "ruff-0.4.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:927b11c1e4d0727ce1a729eace61cee88a334623ec424c0b1c8fe3e5f9d3c865"}, - {file = "ruff-0.4.3-py3-none-win32.whl", hash = "sha256:25cacda2155778beb0d064e0ec5a3944dcca9c12715f7c4634fd9d93ac33fd30"}, - {file = "ruff-0.4.3-py3-none-win_amd64.whl", hash = "sha256:7a1c3a450bc6539ef00da6c819fb1b76b6b065dec585f91456e7c0d6a0bbc725"}, - {file = "ruff-0.4.3-py3-none-win_arm64.whl", hash = "sha256:71ca5f8ccf1121b95a59649482470c5601c60a416bf189d553955b0338e34614"}, - {file = "ruff-0.4.3.tar.gz", hash = "sha256:ff0a3ef2e3c4b6d133fbedcf9586abfbe38d076041f2dc18ffb2c7e0485d5a07"}, + {file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, + {file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, + {file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, + {file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, + {file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, + {file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, ] [[package]] @@ -1853,17 +1836,6 @@ files = [ {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] -[[package]] -name = "tzdata" -version = "2024.1" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, -] - [[package]] name = "uri-template" version = "1.3.0" @@ -2063,4 +2035,4 @@ tests = ["pytest", "pytest-cov"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "f1885d3734003c3ef4591b35fcd92c5b8738f3531a37562591705ae92e16df59" +content-hash = "32e9283427a9fc742dcaeff76059d3535e8071666612263c92a05a3bdb355b1b" diff --git a/pyproject.toml b/pyproject.toml index d3355483..1a8fc052 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,12 +24,12 @@ packages = [ python = "^3.10" fastapi = "^0.110.3" sqlmodel = "^0.0.18" -psycopg = {extras = ["binary"], version = "^3.1.18"} alembic = "^1.13.1" pydantic = "^2.7.1" jinja2 = "^3.1.4" pydantic-settings = "^2.2.1" granian = {extras = ["reload"], version = "^1.3.1"} +psycopg2-binary = "^2.9.9" [tool.poetry.group.test.dependencies] pytest = "^8.1.1" @@ -76,11 +76,18 @@ typeCheckingMode = "strict" reportMissingImports = true reportMissingTypeStubs = true reportUnnecessaryIsInstance = false +exclude = [ + "tad/migrations" +] [tool.coverage.run] branch = true command_line = "-m pytest" relative_files = true # needed for sonarcloud code coverage +omit = [ + "tests/*" +] + [tool.coverage.report] fail_under = 95 @@ -94,6 +101,7 @@ title = "tad" testpaths = [ "tests" ] +addopts = "--strict-markers" filterwarnings = [ "ignore::UserWarning" ] diff --git a/tad/core/config.py b/tad/core/config.py index 2ab11f92..8ead4a88 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -1,5 +1,5 @@ +import logging import secrets -import warnings from typing import Any, TypeVar from pydantic import ( @@ -18,7 +18,10 @@ class Settings(BaseSettings): # todo(berry): investigate yaml, toml or json file support for SettingsConfigDict - model_config = SettingsConfigDict(env_file=".env", env_ignore_empty=True, extra="ignore") + # todo(berry): investigate multiple .env files support for SettingsConfigDict + model_config = SettingsConfigDict( + env_file=(".env", ".env.test", ".env.prod"), env_ignore_empty=True, extra="ignore" + ) SECRET_KEY: str = secrets.token_urlsafe(32) DOMAIN: str = "localhost" @@ -43,23 +46,33 @@ def server_host(self) -> str: # todo(berry): create submodel for database settings APP_DATABASE_SCHEME: DatabaseSchemaType = "sqlite" + APP_DATABASE_DRIVER: str | None = None + APP_DATABASE_SERVER: str = "db" APP_DATABASE_PORT: int = 5432 APP_DATABASE_USER: str = "tad" - APP_DATABASE_PASSWORD: str + APP_DATABASE_PASSWORD: str | None = None APP_DATABASE_DB: str = "tad" - SQLITE_FILE: str = "//./database" + APP_DATABASE_FILE: str = "database.sqlite3" @computed_field # type: ignore[misc] @property def SQLALCHEMY_DATABASE_URI(self) -> str: + logging.info(f"test: {self.APP_DATABASE_SCHEME}") + if self.APP_DATABASE_SCHEME == "sqlite": - return str(MultiHostUrl.build(scheme=self.APP_DATABASE_SCHEME, host="", path=self.SQLITE_FILE)) + return str(MultiHostUrl.build(scheme=self.APP_DATABASE_SCHEME, host="", path=self.APP_DATABASE_FILE)) + + scheme: str = ( + f"{self.APP_DATABASE_SCHEME}+{self.APP_DATABASE_DRIVER}" + if isinstance(self.APP_DATABASE_DRIVER, str) + else self.APP_DATABASE_SCHEME + ) return str( MultiHostUrl.build( - scheme=self.APP_DATABASE_SCHEME, + scheme=scheme, username=self.APP_DATABASE_USER, password=self.APP_DATABASE_PASSWORD, host=self.APP_DATABASE_SERVER, @@ -68,21 +81,6 @@ def SQLALCHEMY_DATABASE_URI(self) -> str: ) ) - def _check_default_secret(self, var_name: str, value: str | None) -> None: - if value == "changethis": - message = f'The value of {var_name} is "changethis", ' "for security, please change it" - if self.ENVIRONMENT == "local": - warnings.warn(message, stacklevel=1) - else: - raise SettingsError(message) - - @model_validator(mode="after") - def _enforce_non_default_secrets(self: SelfSettings) -> SelfSettings: - self._check_default_secret("SECRET_KEY", self.SECRET_KEY) - self._check_default_secret("APP_DATABASE_PASSWORD", self.APP_DATABASE_PASSWORD) - - return self - @model_validator(mode="after") def _enforce_database_rules(self: SelfSettings) -> SelfSettings: if self.ENVIRONMENT != "local" and self.APP_DATABASE_SCHEME == "sqlite": diff --git a/tad/core/db.py b/tad/core/db.py index 1b400351..d5317814 100644 --- a/tad/core/db.py +++ b/tad/core/db.py @@ -1,5 +1,10 @@ -from sqlmodel import create_engine +from sqlmodel import Session, create_engine, select from tad.core.config import settings -engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, echo=True) +engine = create_engine(settings.SQLALCHEMY_DATABASE_URI) + + +async def check_db(): + with Session(engine) as session: + session.exec(select(1)) diff --git a/tad/main.py b/tad/main.py index ea21ff00..7a2390d2 100644 --- a/tad/main.py +++ b/tad/main.py @@ -10,6 +10,7 @@ from tad.api.main import api_router from tad.core.config import settings +from tad.core.db import check_db from tad.core.exception_handlers import ( http_exception_handler as tad_http_exception_handler, ) @@ -32,6 +33,7 @@ async def lifespan(app: FastAPI): logger.info(f"Starting {settings.PROJECT_NAME} version {settings.VERSION}") logger.info(f"Settings: {mask.secrets(settings.model_dump())}") # todo(berry): setup database connection + await check_db() yield logger.info(f"Stopping application {settings.PROJECT_NAME} version {settings.VERSION}") logging.shutdown() diff --git a/tad/models/__init__.py b/tad/models/__init__.py index e69de29b..e8084bec 100644 --- a/tad/models/__init__.py +++ b/tad/models/__init__.py @@ -0,0 +1,3 @@ +from .hero import Hero + +__all__ = ["Hero"] diff --git a/tests/conftest.py b/tests/conftest.py index ba26bced..77538545 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,23 +1,28 @@ -import os from collections.abc import Generator import pytest from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool from tad.main import app +# needed to make sure create_all knows about the models +from tad.models import * # noqa: F403 + -# todo(berry): add database fixtures @pytest.fixture(scope="module") -def client() -> Generator[TestClient, None, None]: +def client(db: Session) -> Generator[TestClient, None, None]: with TestClient(app, raise_server_exceptions=True) as c: - c.timeout = 3 + c.timeout = 5 yield c -@pytest.fixture(autouse=True) -def setup_basic_environmental_variables(monkeypatch: pytest.MonkeyPatch) -> Generator[None, None, None]: # noqa: PT004 - original_environ = dict(os.environ) - monkeypatch.setenv("APP_DATABASE_PASSWORD", "changethis") - yield - os.environ.clear() - os.environ.update(original_environ) +@pytest.fixture(scope="module") +def db() -> Generator[Session, None, None]: + engine = create_engine("sqlite://", poolclass=StaticPool) + SQLModel.metadata.create_all(engine) + + with Session(engine) as session: + yield session + + SQLModel.metadata.drop_all(engine) diff --git a/tests/core/test_config.py b/tests/core/test_config.py index 697f9124..f7c8bbc3 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -1,5 +1,3 @@ -import os - import pytest from tad.core.config import Settings from tad.core.exceptions import SettingsError @@ -16,23 +14,18 @@ def test_default_settings(): assert settings.PROJECT_NAME == "TAD" assert settings.PROJECT_DESCRIPTION == "Transparency of Algorithmic Decision making" assert settings.APP_DATABASE_SCHEME == "sqlite" - assert settings.APP_DATABASE_SERVER == "db" - assert settings.APP_DATABASE_PORT == 5432 - assert settings.APP_DATABASE_USER == "tad" - assert settings.APP_DATABASE_DB == "tad" - assert settings.SQLITE_FILE == "//./database" - assert settings.SQLALCHEMY_DATABASE_URI == "sqlite://///database" - - -def test_environment_settings(): - os.environ["DOMAIN"] = "google.com" - os.environ["ENVIRONMENT"] = "production" - os.environ["PROJECT_NAME"] = "TAD2" - os.environ["SECRET_KEY"] = "mysecret" # noqa: S105 - os.environ["APP_DATABASE_SCHEME"] = "postgresql" - os.environ["APP_DATABASE_USER"] = "tad2" - os.environ["APP_DATABASE_DB"] = "tad2" - os.environ["APP_DATABASE_PASSWORD"] = "mypassword" # noqa: S105 + assert settings.SQLALCHEMY_DATABASE_URI == "sqlite:///database.sqlite3" + + +def test_environment_settings(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("DOMAIN", "google.com") + monkeypatch.setenv("ENVIRONMENT", "production") + monkeypatch.setenv("PROJECT_NAME", "TAD2") + monkeypatch.setenv("SECRET_KEY", "mysecret") + monkeypatch.setenv("APP_DATABASE_SCHEME", "postgresql") + monkeypatch.setenv("APP_DATABASE_USER", "tad2") + monkeypatch.setenv("APP_DATABASE_DB", "tad2") + monkeypatch.setenv("APP_DATABASE_PASSWORD", "mypassword") settings = Settings(_env_file="nonexisitingfile") # type: ignore assert settings.SECRET_KEY == "mysecret" # noqa: S105 @@ -49,22 +42,21 @@ def test_environment_settings(): assert settings.APP_DATABASE_USER == "tad2" assert settings.APP_DATABASE_PASSWORD == "mypassword" # noqa: S105 assert settings.APP_DATABASE_DB == "tad2" - assert settings.SQLITE_FILE == "//./database" assert settings.SQLALCHEMY_DATABASE_URI == "postgresql://tad2:mypassword@db:5432/tad2" -def test_environment_settings_production_sqlite_error(): - os.environ["ENVIRONMENT"] = "production" - os.environ["APP_DATABASE_SCHEME"] = "sqlite" - os.environ["APP_DATABASE_PASSWORD"] = "32452345432" # noqa: S105 +def test_environment_settings_production_sqlite_error(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("ENVIRONMENT", "production") + monkeypatch.setenv("APP_DATABASE_SCHEME", "sqlite") + monkeypatch.setenv("APP_DATABASE_PASSWORD", "32452345432") with pytest.raises(SettingsError) as e: _settings = Settings(_env_file="nonexisitingfile") # type: ignore assert e.value.message == "SQLite is not supported in production" -def test_environment_settings_production_nopassword_error(): - os.environ["ENVIRONMENT"] = "production" +def test_environment_settings_production_nopassword_error(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("ENVIRONMENT", "production") with pytest.raises(SettingsError): _settings = Settings(_env_file="nonexisitingfile") # type: ignore diff --git a/tests/core/test_db.py b/tests/core/test_db.py new file mode 100644 index 00000000..5c8aab81 --- /dev/null +++ b/tests/core/test_db.py @@ -0,0 +1,15 @@ +from unittest.mock import Mock, patch + +import pytest +from sqlmodel import Session, select +from tad.core.db import check_db + + +@pytest.mark.skip(reason="not working yet") +async def test_check_dabase(): + mock_session = Mock(spec=Session) + + with patch("sqlmodel.Session", return_value=mock_session): + await check_db() + + assert mock_session.exec.assert_called_once_with(select(1)) diff --git a/tests/core/test_exception_handlers.py b/tests/core/test_exception_handlers.py index 91e2d0b2..2c3c41e4 100644 --- a/tests/core/test_exception_handlers.py +++ b/tests/core/test_exception_handlers.py @@ -1,3 +1,5 @@ +from typing import Any + import pytest from fastapi import status from fastapi.exceptions import HTTPException, RequestValidationError @@ -6,28 +8,26 @@ @pytest.fixture() -def _mock_error_500(): +def mock_error_500() -> Any: # noqa: PT004 """Add route to raise ValueError""" @app.get("/raise-http-exception") - async def raise_http_error(): + async def _raise_http_error(): # type: ignore raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) @app.get("/raise-request-validation-exception") - async def raise_request_error(): - raise RequestValidationError(errors=None) + async def _raise_request_error(): # type: ignore + raise RequestValidationError(errors="None") -@pytest.mark.usefixtures("_mock_error_500") -def test_http_exception_handler(client: TestClient): +def test_http_exception_handler(client: TestClient, mock_error_500: Any): response = client.get("/raise-http-exception") assert response.status_code == status.HTTP_404_NOT_FOUND assert response.headers["content-type"] == "text/html; charset=utf-8" -@pytest.mark.usefixtures("_mock_error_500") -def test_request_validation_exception_handler(client: TestClient): +def test_request_validation_exception_handler(client: TestClient, mock_error_500: Any): response = client.get("/raise-request-validation-exception") assert response.status_code == status.HTTP_400_BAD_REQUEST From c570ef20f6a8e8ea19b5628defe49e4dfacc2b01 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Mon, 13 May 2024 14:47:24 +0000 Subject: [PATCH 44/55] Add basic template setup --- tad/api/deps.py | 19 +++++++++++++++++++ tad/api/routes/root.py | 8 +++++--- tad/core/config.py | 1 + tad/static/css/styles.css | 0 tad/static/js/main.js | 0 tad/templates/layout.html | 21 +++++++++++++++++++++ tad/templates/root/index.html | 8 ++++++++ tests/api/routes/test_root.py | 2 ++ tests/api/routes/test_static.py | 14 ++++++++++++++ 9 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tad/api/deps.py create mode 100644 tad/static/css/styles.css create mode 100644 tad/static/js/main.js create mode 100644 tad/templates/layout.html create mode 100644 tad/templates/root/index.html create mode 100644 tests/api/routes/test_static.py diff --git a/tad/api/deps.py b/tad/api/deps.py new file mode 100644 index 00000000..1a7c8622 --- /dev/null +++ b/tad/api/deps.py @@ -0,0 +1,19 @@ +from collections.abc import Generator +from typing import Annotated + +from fastapi import Depends +from fastapi.templating import Jinja2Templates +from sqlmodel import Session + +from tad.core.config import settings +from tad.core.db import engine + +templates = Jinja2Templates(directory=settings.TEMPLATE_DIR) + + +def get_db() -> Generator[Session, None, None]: + with Session(engine) as session: + yield session + + +SessionDep = Annotated[Session, Depends(get_db)] diff --git a/tad/api/routes/root.py b/tad/api/routes/root.py index 2aa20e12..a33a202e 100644 --- a/tad/api/routes/root.py +++ b/tad/api/routes/root.py @@ -1,9 +1,11 @@ -from fastapi import APIRouter +from fastapi import APIRouter, Request from fastapi.responses import HTMLResponse +from tad.api.deps import templates + router = APIRouter() @router.get("/") -async def base(): - return HTMLResponse(content="message - Hello World 2 sdfasdfsda") +async def base(request: Request) -> HTMLResponse: + return templates.TemplateResponse(request=request, name="root/index.html") diff --git a/tad/core/config.py b/tad/core/config.py index 8ead4a88..0023c856 100644 --- a/tad/core/config.py +++ b/tad/core/config.py @@ -43,6 +43,7 @@ def server_host(self) -> str: PROJECT_DESCRIPTION: str = "Transparency of Algorithmic Decision making" STATIC_DIR: str = "tad/static" + TEMPLATE_DIR: str = "tad/templates" # todo(berry): create submodel for database settings APP_DATABASE_SCHEME: DatabaseSchemaType = "sqlite" diff --git a/tad/static/css/styles.css b/tad/static/css/styles.css new file mode 100644 index 00000000..e69de29b diff --git a/tad/static/js/main.js b/tad/static/js/main.js new file mode 100644 index 00000000..e69de29b diff --git a/tad/templates/layout.html b/tad/templates/layout.html new file mode 100644 index 00000000..1f613521 --- /dev/null +++ b/tad/templates/layout.html @@ -0,0 +1,21 @@ + + + + + + + {% block title %}{% endblock %} + + + + + + {% block head %}{% endblock %} + + + + + {% block content %}{% endblock %} + + + diff --git a/tad/templates/root/index.html b/tad/templates/root/index.html new file mode 100644 index 00000000..ce9e75d6 --- /dev/null +++ b/tad/templates/root/index.html @@ -0,0 +1,8 @@ +{% extends "layout.html" %} + +{% block title %}Home Page{% endblock %} + +{% block content %} +

Welcome to the Home Page

+

This is a simple Jinja template example.

+{% endblock %} diff --git a/tests/api/routes/test_root.py b/tests/api/routes/test_root.py index 80871570..d0930ab1 100644 --- a/tests/api/routes/test_root.py +++ b/tests/api/routes/test_root.py @@ -7,3 +7,5 @@ def test_get_root(client: TestClient) -> None: ) assert response.status_code == 200 assert response.headers["content-type"] == "text/html; charset=utf-8" + + assert b"

Welcome to the Home Page

" in response.content diff --git a/tests/api/routes/test_static.py b/tests/api/routes/test_static.py new file mode 100644 index 00000000..b2d1a367 --- /dev/null +++ b/tests/api/routes/test_static.py @@ -0,0 +1,14 @@ +import pytest +from fastapi.testclient import TestClient + + +@pytest.mark.skip(reason="Not working yet") +def test_static_css(client: TestClient) -> None: + response = client.get("/static/styles.css") + assert response.status_code == 200 + + +@pytest.mark.skip(reason="Not working yet") +def test_static_js(client: TestClient) -> None: + response = client.get("/static/main.js") + assert response.status_code == 200 From 11c523bb41f7b54b75f6a89f0f62a790e01bfba1 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Mon, 13 May 2024 15:15:00 +0000 Subject: [PATCH 45/55] Add htmx --- .pre-commit-config.yaml | 1 + tad/static/vendor/htmx/1.9.12.min.js | 1 + tad/templates/layout.html | 2 ++ 3 files changed, 4 insertions(+) create mode 100644 tad/static/vendor/htmx/1.9.12.min.js diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af8a30ea..4290a1e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,6 +5,7 @@ repos: rev: v4.6.0 hooks: - id: end-of-file-fixer + exclude: ^tad/static/vendor/* - id: trailing-whitespace - id: check-yaml - id: check-json diff --git a/tad/static/vendor/htmx/1.9.12.min.js b/tad/static/vendor/htmx/1.9.12.min.js new file mode 100644 index 00000000..52866033 --- /dev/null +++ b/tad/static/vendor/htmx/1.9.12.min.js @@ -0,0 +1 @@ +(function (e, t) { if (typeof define === "function" && define.amd) { define([], t) } else if (typeof module === "object" && module.exports) { module.exports = t() } else { e.htmx = e.htmx || t() } })(typeof self !== "undefined" ? self : this, function () { return function () { "use strict"; var Q = { onLoad: F, process: zt, on: de, off: ge, trigger: ce, ajax: Nr, find: C, findAll: f, closest: v, values: function (e, t) { var r = dr(e, t || "post"); return r.values }, remove: _, addClass: z, removeClass: n, toggleClass: $, takeClass: W, defineExtension: Ur, removeExtension: Br, logAll: V, logNone: j, logger: null, config: { historyEnabled: true, historyCacheSize: 10, refreshOnHistoryMiss: false, defaultSwapStyle: "innerHTML", defaultSwapDelay: 0, defaultSettleDelay: 20, includeIndicatorStyles: true, indicatorClass: "htmx-indicator", requestClass: "htmx-request", addedClass: "htmx-added", settlingClass: "htmx-settling", swappingClass: "htmx-swapping", allowEval: true, allowScriptTags: true, inlineScriptNonce: "", attributesToSettle: ["class", "style", "width", "height"], withCredentials: false, timeout: 0, wsReconnectDelay: "full-jitter", wsBinaryType: "blob", disableSelector: "[hx-disable], [data-hx-disable]", useTemplateFragments: false, scrollBehavior: "smooth", defaultFocusScroll: false, getCacheBusterParam: false, globalViewTransitions: false, methodsThatUseUrlParams: ["get"], selfRequestsOnly: false, ignoreTitle: false, scrollIntoViewOnBoost: true, triggerSpecsCache: null }, parseInterval: d, _: t, createEventSource: function (e) { return new EventSource(e, { withCredentials: true }) }, createWebSocket: function (e) { var t = new WebSocket(e, []); t.binaryType = Q.config.wsBinaryType; return t }, version: "1.9.12" }; var r = { addTriggerHandler: Lt, bodyContains: se, canAccessLocalStorage: U, findThisElement: xe, filterValues: yr, hasAttribute: o, getAttributeValue: te, getClosestAttributeValue: ne, getClosestMatch: c, getExpressionVars: Hr, getHeaders: xr, getInputValues: dr, getInternalData: ae, getSwapSpecification: wr, getTriggerSpecs: it, getTarget: ye, makeFragment: l, mergeObjects: le, makeSettleInfo: T, oobSwap: Ee, querySelectorExt: ue, selectAndSwap: je, settleImmediately: nr, shouldCancel: ut, triggerEvent: ce, triggerErrorEvent: fe, withExtensions: R }; var w = ["get", "post", "put", "delete", "patch"]; var i = w.map(function (e) { return "[hx-" + e + "], [data-hx-" + e + "]" }).join(", "); var S = e("head"), q = e("title"), H = e("svg", true); function e(e, t) { return new RegExp("<" + e + "(\\s[^>]*>|>)([\\s\\S]*?)<\\/" + e + ">", !!t ? "gim" : "im") } function d(e) { if (e == undefined) { return undefined } let t = NaN; if (e.slice(-2) == "ms") { t = parseFloat(e.slice(0, -2)) } else if (e.slice(-1) == "s") { t = parseFloat(e.slice(0, -1)) * 1e3 } else if (e.slice(-1) == "m") { t = parseFloat(e.slice(0, -1)) * 1e3 * 60 } else { t = parseFloat(e) } return isNaN(t) ? undefined : t } function ee(e, t) { return e.getAttribute && e.getAttribute(t) } function o(e, t) { return e.hasAttribute && (e.hasAttribute(t) || e.hasAttribute("data-" + t)) } function te(e, t) { return ee(e, t) || ee(e, "data-" + t) } function u(e) { return e.parentElement } function re() { return document } function c(e, t) { while (e && !t(e)) { e = u(e) } return e ? e : null } function L(e, t, r) { var n = te(t, r); var i = te(t, "hx-disinherit"); if (e !== t && i && (i === "*" || i.split(" ").indexOf(r) >= 0)) { return "unset" } else { return n } } function ne(t, r) { var n = null; c(t, function (e) { return n = L(t, e, r) }); if (n !== "unset") { return n } } function h(e, t) { var r = e.matches || e.matchesSelector || e.msMatchesSelector || e.mozMatchesSelector || e.webkitMatchesSelector || e.oMatchesSelector; return r && r.call(e, t) } function A(e) { var t = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i; var r = t.exec(e); if (r) { return r[1].toLowerCase() } else { return "" } } function s(e, t) { var r = new DOMParser; var n = r.parseFromString(e, "text/html"); var i = n.body; while (t > 0) { t--; i = i.firstChild } if (i == null) { i = re().createDocumentFragment() } return i } function N(e) { return /", 0); var a = i.querySelector("template").content; if (Q.config.allowScriptTags) { oe(a.querySelectorAll("script"), function (e) { if (Q.config.inlineScriptNonce) { e.nonce = Q.config.inlineScriptNonce } e.htmxExecuted = navigator.userAgent.indexOf("Firefox") === -1 }) } else { oe(a.querySelectorAll("script"), function (e) { _(e) }) } return a } switch (r) { case "thead": case "tbody": case "tfoot": case "colgroup": case "caption": return s("" + n + "
", 1); case "col": return s("" + n + "
", 2); case "tr": return s("" + n + "
", 2); case "td": case "th": return s("" + n + "
", 3); case "script": case "style": return s("
" + n + "
", 1); default: return s(n, 0) } } function ie(e) { if (e) { e() } } function I(e, t) { return Object.prototype.toString.call(e) === "[object " + t + "]" } function k(e) { return I(e, "Function") } function P(e) { return I(e, "Object") } function ae(e) { var t = "htmx-internal-data"; var r = e[t]; if (!r) { r = e[t] = {} } return r } function M(e) { var t = []; if (e) { for (var r = 0; r < e.length; r++) { t.push(e[r]) } } return t } function oe(e, t) { if (e) { for (var r = 0; r < e.length; r++) { t(e[r]) } } } function X(e) { var t = e.getBoundingClientRect(); var r = t.top; var n = t.bottom; return r < window.innerHeight && n >= 0 } function se(e) { if (e.getRootNode && e.getRootNode() instanceof window.ShadowRoot) { return re().body.contains(e.getRootNode().host) } else { return re().body.contains(e) } } function D(e) { return e.trim().split(/\s+/) } function le(e, t) { for (var r in t) { if (t.hasOwnProperty(r)) { e[r] = t[r] } } return e } function E(e) { try { return JSON.parse(e) } catch (e) { b(e); return null } } function U() { var e = "htmx:localStorageTest"; try { localStorage.setItem(e, e); localStorage.removeItem(e); return true } catch (e) { return false } } function B(t) { try { var e = new URL(t); if (e) { t = e.pathname + e.search } if (!/^\/$/.test(t)) { t = t.replace(/\/+$/, "") } return t } catch (e) { return t } } function t(e) { return Tr(re().body, function () { return eval(e) }) } function F(t) { var e = Q.on("htmx:load", function (e) { t(e.detail.elt) }); return e } function V() { Q.logger = function (e, t, r) { if (console) { console.log(t, e, r) } } } function j() { Q.logger = null } function C(e, t) { if (t) { return e.querySelector(t) } else { return C(re(), e) } } function f(e, t) { if (t) { return e.querySelectorAll(t) } else { return f(re(), e) } } function _(e, t) { e = p(e); if (t) { setTimeout(function () { _(e); e = null }, t) } else { e.parentElement.removeChild(e) } } function z(e, t, r) { e = p(e); if (r) { setTimeout(function () { z(e, t); e = null }, r) } else { e.classList && e.classList.add(t) } } function n(e, t, r) { e = p(e); if (r) { setTimeout(function () { n(e, t); e = null }, r) } else { if (e.classList) { e.classList.remove(t); if (e.classList.length === 0) { e.removeAttribute("class") } } } } function $(e, t) { e = p(e); e.classList.toggle(t) } function W(e, t) { e = p(e); oe(e.parentElement.children, function (e) { n(e, t) }); z(e, t) } function v(e, t) { e = p(e); if (e.closest) { return e.closest(t) } else { do { if (e == null || h(e, t)) { return e } } while (e = e && u(e)); return null } } function g(e, t) { return e.substring(0, t.length) === t } function G(e, t) { return e.substring(e.length - t.length) === t } function J(e) { var t = e.trim(); if (g(t, "<") && G(t, "/>")) { return t.substring(1, t.length - 2) } else { return t } } function Z(e, t) { if (t.indexOf("closest ") === 0) { return [v(e, J(t.substr(8)))] } else if (t.indexOf("find ") === 0) { return [C(e, J(t.substr(5)))] } else if (t === "next") { return [e.nextElementSibling] } else if (t.indexOf("next ") === 0) { return [K(e, J(t.substr(5)))] } else if (t === "previous") { return [e.previousElementSibling] } else if (t.indexOf("previous ") === 0) { return [Y(e, J(t.substr(9)))] } else if (t === "document") { return [document] } else if (t === "window") { return [window] } else if (t === "body") { return [document.body] } else { return re().querySelectorAll(J(t)) } } var K = function (e, t) { var r = re().querySelectorAll(t); for (var n = 0; n < r.length; n++) { var i = r[n]; if (i.compareDocumentPosition(e) === Node.DOCUMENT_POSITION_PRECEDING) { return i } } }; var Y = function (e, t) { var r = re().querySelectorAll(t); for (var n = r.length - 1; n >= 0; n--) { var i = r[n]; if (i.compareDocumentPosition(e) === Node.DOCUMENT_POSITION_FOLLOWING) { return i } } }; function ue(e, t) { if (t) { return Z(e, t)[0] } else { return Z(re().body, e)[0] } } function p(e) { if (I(e, "String")) { return C(e) } else { return e } } function ve(e, t, r) { if (k(t)) { return { target: re().body, event: e, listener: t } } else { return { target: p(e), event: t, listener: r } } } function de(t, r, n) { jr(function () { var e = ve(t, r, n); e.target.addEventListener(e.event, e.listener) }); var e = k(r); return e ? r : n } function ge(t, r, n) { jr(function () { var e = ve(t, r, n); e.target.removeEventListener(e.event, e.listener) }); return k(r) ? r : n } var pe = re().createElement("output"); function me(e, t) { var r = ne(e, t); if (r) { if (r === "this") { return [xe(e, t)] } else { var n = Z(e, r); if (n.length === 0) { b('The selector "' + r + '" on ' + t + " returned no matches!"); return [pe] } else { return n } } } } function xe(e, t) { return c(e, function (e) { return te(e, t) != null }) } function ye(e) { var t = ne(e, "hx-target"); if (t) { if (t === "this") { return xe(e, "hx-target") } else { return ue(e, t) } } else { var r = ae(e); if (r.boosted) { return re().body } else { return e } } } function be(e) { var t = Q.config.attributesToSettle; for (var r = 0; r < t.length; r++) { if (e === t[r]) { return true } } return false } function we(t, r) { oe(t.attributes, function (e) { if (!r.hasAttribute(e.name) && be(e.name)) { t.removeAttribute(e.name) } }); oe(r.attributes, function (e) { if (be(e.name)) { t.setAttribute(e.name, e.value) } }) } function Se(e, t) { var r = Fr(t); for (var n = 0; n < r.length; n++) { var i = r[n]; try { if (i.isInlineSwap(e)) { return true } } catch (e) { b(e) } } return e === "outerHTML" } function Ee(e, i, a) { var t = "#" + ee(i, "id"); var o = "outerHTML"; if (e === "true") { } else if (e.indexOf(":") > 0) { o = e.substr(0, e.indexOf(":")); t = e.substr(e.indexOf(":") + 1, e.length) } else { o = e } var r = re().querySelectorAll(t); if (r) { oe(r, function (e) { var t; var r = i.cloneNode(true); t = re().createDocumentFragment(); t.appendChild(r); if (!Se(o, e)) { t = r } var n = { shouldSwap: true, target: e, fragment: t }; if (!ce(e, "htmx:oobBeforeSwap", n)) return; e = n.target; if (n["shouldSwap"]) { Fe(o, e, e, t, a) } oe(a.elts, function (e) { ce(e, "htmx:oobAfterSwap", n) }) }); i.parentNode.removeChild(i) } else { i.parentNode.removeChild(i); fe(re().body, "htmx:oobErrorNoTarget", { content: i }) } return e } function Ce(e, t, r) { var n = ne(e, "hx-select-oob"); if (n) { var i = n.split(","); for (var a = 0; a < i.length; a++) { var o = i[a].split(":", 2); var s = o[0].trim(); if (s.indexOf("#") === 0) { s = s.substring(1) } var l = o[1] || "true"; var u = t.querySelector("#" + s); if (u) { Ee(l, u, r) } } } oe(f(t, "[hx-swap-oob], [data-hx-swap-oob]"), function (e) { var t = te(e, "hx-swap-oob"); if (t != null) { Ee(t, e, r) } }) } function Re(e) { oe(f(e, "[hx-preserve], [data-hx-preserve]"), function (e) { var t = te(e, "id"); var r = re().getElementById(t); if (r != null) { e.parentNode.replaceChild(r, e) } }) } function Te(o, e, s) { oe(e.querySelectorAll("[id]"), function (e) { var t = ee(e, "id"); if (t && t.length > 0) { var r = t.replace("'", "\\'"); var n = e.tagName.replace(":", "\\:"); var i = o.querySelector(n + "[id='" + r + "']"); if (i && i !== o) { var a = e.cloneNode(); we(e, i); s.tasks.push(function () { we(e, a) }) } } }) } function Oe(e) { return function () { n(e, Q.config.addedClass); zt(e); Nt(e); qe(e); ce(e, "htmx:load") } } function qe(e) { var t = "[autofocus]"; var r = h(e, t) ? e : e.querySelector(t); if (r != null) { r.focus() } } function a(e, t, r, n) { Te(e, r, n); while (r.childNodes.length > 0) { var i = r.firstChild; z(i, Q.config.addedClass); e.insertBefore(i, t); if (i.nodeType !== Node.TEXT_NODE && i.nodeType !== Node.COMMENT_NODE) { n.tasks.push(Oe(i)) } } } function He(e, t) { var r = 0; while (r < e.length) { t = (t << 5) - t + e.charCodeAt(r++) | 0 } return t } function Le(e) { var t = 0; if (e.attributes) { for (var r = 0; r < e.attributes.length; r++) { var n = e.attributes[r]; if (n.value) { t = He(n.name, t); t = He(n.value, t) } } } return t } function Ae(e) { var t = ae(e); if (t.onHandlers) { for (var r = 0; r < t.onHandlers.length; r++) { const n = t.onHandlers[r]; e.removeEventListener(n.event, n.listener) } delete t.onHandlers } } function Ne(e) { var t = ae(e); if (t.timeout) { clearTimeout(t.timeout) } if (t.webSocket) { t.webSocket.close() } if (t.sseEventSource) { t.sseEventSource.close() } if (t.listenerInfos) { oe(t.listenerInfos, function (e) { if (e.on) { e.on.removeEventListener(e.trigger, e.listener) } }) } Ae(e); oe(Object.keys(t), function (e) { delete t[e] }) } function m(e) { ce(e, "htmx:beforeCleanupElement"); Ne(e); if (e.children) { oe(e.children, function (e) { m(e) }) } } function Ie(t, e, r) { if (t.tagName === "BODY") { return Ue(t, e, r) } else { var n; var i = t.previousSibling; a(u(t), t, e, r); if (i == null) { n = u(t).firstChild } else { n = i.nextSibling } r.elts = r.elts.filter(function (e) { return e != t }); while (n && n !== t) { if (n.nodeType === Node.ELEMENT_NODE) { r.elts.push(n) } n = n.nextElementSibling } m(t); u(t).removeChild(t) } } function ke(e, t, r) { return a(e, e.firstChild, t, r) } function Pe(e, t, r) { return a(u(e), e, t, r) } function Me(e, t, r) { return a(e, null, t, r) } function Xe(e, t, r) { return a(u(e), e.nextSibling, t, r) } function De(e, t, r) { m(e); return u(e).removeChild(e) } function Ue(e, t, r) { var n = e.firstChild; a(e, n, t, r); if (n) { while (n.nextSibling) { m(n.nextSibling); e.removeChild(n.nextSibling) } m(n); e.removeChild(n) } } function Be(e, t, r) { var n = r || ne(e, "hx-select"); if (n) { var i = re().createDocumentFragment(); oe(t.querySelectorAll(n), function (e) { i.appendChild(e) }); t = i } return t } function Fe(e, t, r, n, i) { switch (e) { case "none": return; case "outerHTML": Ie(r, n, i); return; case "afterbegin": ke(r, n, i); return; case "beforebegin": Pe(r, n, i); return; case "beforeend": Me(r, n, i); return; case "afterend": Xe(r, n, i); return; case "delete": De(r, n, i); return; default: var a = Fr(t); for (var o = 0; o < a.length; o++) { var s = a[o]; try { var l = s.handleSwap(e, r, n, i); if (l) { if (typeof l.length !== "undefined") { for (var u = 0; u < l.length; u++) { var f = l[u]; if (f.nodeType !== Node.TEXT_NODE && f.nodeType !== Node.COMMENT_NODE) { i.tasks.push(Oe(f)) } } } return } } catch (e) { b(e) } } if (e === "innerHTML") { Ue(r, n, i) } else { Fe(Q.config.defaultSwapStyle, t, r, n, i) } } } function Ve(e) { if (e.indexOf(" -1) { var t = e.replace(H, ""); var r = t.match(q); if (r) { return r[2] } } } function je(e, t, r, n, i, a) { i.title = Ve(n); var o = l(n); if (o) { Ce(r, o, i); o = Be(r, o, a); Re(o); return Fe(e, r, t, o, i) } } function _e(e, t, r) { var n = e.getResponseHeader(t); if (n.indexOf("{") === 0) { var i = E(n); for (var a in i) { if (i.hasOwnProperty(a)) { var o = i[a]; if (!P(o)) { o = { value: o } } ce(r, a, o) } } } else { var s = n.split(","); for (var l = 0; l < s.length; l++) { ce(r, s[l].trim(), []) } } } var ze = /\s/; var x = /[\s,]/; var $e = /[_$a-zA-Z]/; var We = /[_$a-zA-Z0-9]/; var Ge = ['"', "'", "/"]; var Je = /[^\s]/; var Ze = /[{(]/; var Ke = /[})]/; function Ye(e) { var t = []; var r = 0; while (r < e.length) { if ($e.exec(e.charAt(r))) { var n = r; while (We.exec(e.charAt(r + 1))) { r++ } t.push(e.substr(n, r - n + 1)) } else if (Ge.indexOf(e.charAt(r)) !== -1) { var i = e.charAt(r); var n = r; r++; while (r < e.length && e.charAt(r) !== i) { if (e.charAt(r) === "\\") { r++ } r++ } t.push(e.substr(n, r - n + 1)) } else { var a = e.charAt(r); t.push(a) } r++ } return t } function Qe(e, t, r) { return $e.exec(e.charAt(0)) && e !== "true" && e !== "false" && e !== "this" && e !== r && t !== "." } function et(e, t, r) { if (t[0] === "[") { t.shift(); var n = 1; var i = " return (function(" + r + "){ return ("; var a = null; while (t.length > 0) { var o = t[0]; if (o === "]") { n--; if (n === 0) { if (a === null) { i = i + "true" } t.shift(); i += ")})"; try { var s = Tr(e, function () { return Function(i)() }, function () { return true }); s.source = i; return s } catch (e) { fe(re().body, "htmx:syntax:error", { error: e, source: i }); return null } } } else if (o === "[") { n++ } if (Qe(o, a, r)) { i += "((" + r + "." + o + ") ? (" + r + "." + o + ") : (window." + o + "))" } else { i = i + o } a = t.shift() } } } function y(e, t) { var r = ""; while (e.length > 0 && !t.test(e[0])) { r += e.shift() } return r } function tt(e) { var t; if (e.length > 0 && Ze.test(e[0])) { e.shift(); t = y(e, Ke).trim(); e.shift() } else { t = y(e, x) } return t } var rt = "input, textarea, select"; function nt(e, t, r) { var n = []; var i = Ye(t); do { y(i, Je); var a = i.length; var o = y(i, /[,\[\s]/); if (o !== "") { if (o === "every") { var s = { trigger: "every" }; y(i, Je); s.pollInterval = d(y(i, /[,\[\s]/)); y(i, Je); var l = et(e, i, "event"); if (l) { s.eventFilter = l } n.push(s) } else if (o.indexOf("sse:") === 0) { n.push({ trigger: "sse", sseEvent: o.substr(4) }) } else { var u = { trigger: o }; var l = et(e, i, "event"); if (l) { u.eventFilter = l } while (i.length > 0 && i[0] !== ",") { y(i, Je); var f = i.shift(); if (f === "changed") { u.changed = true } else if (f === "once") { u.once = true } else if (f === "consume") { u.consume = true } else if (f === "delay" && i[0] === ":") { i.shift(); u.delay = d(y(i, x)) } else if (f === "from" && i[0] === ":") { i.shift(); if (Ze.test(i[0])) { var c = tt(i) } else { var c = y(i, x); if (c === "closest" || c === "find" || c === "next" || c === "previous") { i.shift(); var h = tt(i); if (h.length > 0) { c += " " + h } } } u.from = c } else if (f === "target" && i[0] === ":") { i.shift(); u.target = tt(i) } else if (f === "throttle" && i[0] === ":") { i.shift(); u.throttle = d(y(i, x)) } else if (f === "queue" && i[0] === ":") { i.shift(); u.queue = y(i, x) } else if (f === "root" && i[0] === ":") { i.shift(); u[f] = tt(i) } else if (f === "threshold" && i[0] === ":") { i.shift(); u[f] = y(i, x) } else { fe(e, "htmx:syntax:error", { token: i.shift() }) } } n.push(u) } } if (i.length === a) { fe(e, "htmx:syntax:error", { token: i.shift() }) } y(i, Je) } while (i[0] === "," && i.shift()); if (r) { r[t] = n } return n } function it(e) { var t = te(e, "hx-trigger"); var r = []; if (t) { var n = Q.config.triggerSpecsCache; r = n && n[t] || nt(e, t, n) } if (r.length > 0) { return r } else if (h(e, "form")) { return [{ trigger: "submit" }] } else if (h(e, 'input[type="button"], input[type="submit"]')) { return [{ trigger: "click" }] } else if (h(e, rt)) { return [{ trigger: "change" }] } else { return [{ trigger: "click" }] } } function at(e) { ae(e).cancelled = true } function ot(e, t, r) { var n = ae(e); n.timeout = setTimeout(function () { if (se(e) && n.cancelled !== true) { if (!ct(r, e, Wt("hx:poll:trigger", { triggerSpec: r, target: e }))) { t(e) } ot(e, t, r) } }, r.pollInterval) } function st(e) { return location.hostname === e.hostname && ee(e, "href") && ee(e, "href").indexOf("#") !== 0 } function lt(t, r, e) { if (t.tagName === "A" && st(t) && (t.target === "" || t.target === "_self") || t.tagName === "FORM") { r.boosted = true; var n, i; if (t.tagName === "A") { n = "get"; i = ee(t, "href") } else { var a = ee(t, "method"); n = a ? a.toLowerCase() : "get"; if (n === "get") { } i = ee(t, "action") } e.forEach(function (e) { ht(t, function (e, t) { if (v(e, Q.config.disableSelector)) { m(e); return } he(n, i, e, t) }, r, e, true) }) } } function ut(e, t) { if (e.type === "submit" || e.type === "click") { if (t.tagName === "FORM") { return true } if (h(t, 'input[type="submit"], button') && v(t, "form") !== null) { return true } if (t.tagName === "A" && t.href && (t.getAttribute("href") === "#" || t.getAttribute("href").indexOf("#") !== 0)) { return true } } return false } function ft(e, t) { return ae(e).boosted && e.tagName === "A" && t.type === "click" && (t.ctrlKey || t.metaKey) } function ct(e, t, r) { var n = e.eventFilter; if (n) { try { return n.call(t, r) !== true } catch (e) { fe(re().body, "htmx:eventFilter:error", { error: e, source: n.source }); return true } } return false } function ht(a, o, e, s, l) { var u = ae(a); var t; if (s.from) { t = Z(a, s.from) } else { t = [a] } if (s.changed) { t.forEach(function (e) { var t = ae(e); t.lastValue = e.value }) } oe(t, function (n) { var i = function (e) { if (!se(a)) { n.removeEventListener(s.trigger, i); return } if (ft(a, e)) { return } if (l || ut(e, a)) { e.preventDefault() } if (ct(s, a, e)) { return } var t = ae(e); t.triggerSpec = s; if (t.handledFor == null) { t.handledFor = [] } if (t.handledFor.indexOf(a) < 0) { t.handledFor.push(a); if (s.consume) { e.stopPropagation() } if (s.target && e.target) { if (!h(e.target, s.target)) { return } } if (s.once) { if (u.triggeredOnce) { return } else { u.triggeredOnce = true } } if (s.changed) { var r = ae(n); if (r.lastValue === n.value) { return } r.lastValue = n.value } if (u.delayed) { clearTimeout(u.delayed) } if (u.throttle) { return } if (s.throttle > 0) { if (!u.throttle) { o(a, e); u.throttle = setTimeout(function () { u.throttle = null }, s.throttle) } } else if (s.delay > 0) { u.delayed = setTimeout(function () { o(a, e) }, s.delay) } else { ce(a, "htmx:trigger"); o(a, e) } } }; if (e.listenerInfos == null) { e.listenerInfos = [] } e.listenerInfos.push({ trigger: s.trigger, listener: i, on: n }); n.addEventListener(s.trigger, i) }) } var vt = false; var dt = null; function gt() { if (!dt) { dt = function () { vt = true }; window.addEventListener("scroll", dt); setInterval(function () { if (vt) { vt = false; oe(re().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"), function (e) { pt(e) }) } }, 200) } } function pt(t) { if (!o(t, "data-hx-revealed") && X(t)) { t.setAttribute("data-hx-revealed", "true"); var e = ae(t); if (e.initHash) { ce(t, "revealed") } else { t.addEventListener("htmx:afterProcessNode", function (e) { ce(t, "revealed") }, { once: true }) } } } function mt(e, t, r) { var n = D(r); for (var i = 0; i < n.length; i++) { var a = n[i].split(/:(.+)/); if (a[0] === "connect") { xt(e, a[1], 0) } if (a[0] === "send") { bt(e) } } } function xt(s, r, n) { if (!se(s)) { return } if (r.indexOf("/") == 0) { var e = location.hostname + (location.port ? ":" + location.port : ""); if (location.protocol == "https:") { r = "wss://" + e + r } else if (location.protocol == "http:") { r = "ws://" + e + r } } var t = Q.createWebSocket(r); t.onerror = function (e) { fe(s, "htmx:wsError", { error: e, socket: t }); yt(s) }; t.onclose = function (e) { if ([1006, 1012, 1013].indexOf(e.code) >= 0) { var t = wt(n); setTimeout(function () { xt(s, r, n + 1) }, t) } }; t.onopen = function (e) { n = 0 }; ae(s).webSocket = t; t.addEventListener("message", function (e) { if (yt(s)) { return } var t = e.data; R(s, function (e) { t = e.transformResponse(t, null, s) }); var r = T(s); var n = l(t); var i = M(n.children); for (var a = 0; a < i.length; a++) { var o = i[a]; Ee(te(o, "hx-swap-oob") || "true", o, r) } nr(r.tasks) }) } function yt(e) { if (!se(e)) { ae(e).webSocket.close(); return true } } function bt(u) { var f = c(u, function (e) { return ae(e).webSocket != null }); if (f) { u.addEventListener(it(u)[0].trigger, function (e) { var t = ae(f).webSocket; var r = xr(u, f); var n = dr(u, "post"); var i = n.errors; var a = n.values; var o = Hr(u); var s = le(a, o); var l = yr(s, u); l["HEADERS"] = r; if (i && i.length > 0) { ce(u, "htmx:validation:halted", i); return } t.send(JSON.stringify(l)); if (ut(e, u)) { e.preventDefault() } }) } else { fe(u, "htmx:noWebSocketSourceError") } } function wt(e) { var t = Q.config.wsReconnectDelay; if (typeof t === "function") { return t(e) } if (t === "full-jitter") { var r = Math.min(e, 6); var n = 1e3 * Math.pow(2, r); return n * Math.random() } b('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"') } function St(e, t, r) { var n = D(r); for (var i = 0; i < n.length; i++) { var a = n[i].split(/:(.+)/); if (a[0] === "connect") { Et(e, a[1]) } if (a[0] === "swap") { Ct(e, a[1]) } } } function Et(t, e) { var r = Q.createEventSource(e); r.onerror = function (e) { fe(t, "htmx:sseError", { error: e, source: r }); Tt(t) }; ae(t).sseEventSource = r } function Ct(a, o) { var s = c(a, Ot); if (s) { var l = ae(s).sseEventSource; var u = function (e) { if (Tt(s)) { return } if (!se(a)) { l.removeEventListener(o, u); return } var t = e.data; R(a, function (e) { t = e.transformResponse(t, null, a) }); var r = wr(a); var n = ye(a); var i = T(a); je(r.swapStyle, n, a, t, i); nr(i.tasks); ce(a, "htmx:sseMessage", e) }; ae(a).sseListener = u; l.addEventListener(o, u) } else { fe(a, "htmx:noSSESourceError") } } function Rt(e, t, r) { var n = c(e, Ot); if (n) { var i = ae(n).sseEventSource; var a = function () { if (!Tt(n)) { if (se(e)) { t(e) } else { i.removeEventListener(r, a) } } }; ae(e).sseListener = a; i.addEventListener(r, a) } else { fe(e, "htmx:noSSESourceError") } } function Tt(e) { if (!se(e)) { ae(e).sseEventSource.close(); return true } } function Ot(e) { return ae(e).sseEventSource != null } function qt(e, t, r, n) { var i = function () { if (!r.loaded) { r.loaded = true; t(e) } }; if (n > 0) { setTimeout(i, n) } else { i() } } function Ht(t, i, e) { var a = false; oe(w, function (r) { if (o(t, "hx-" + r)) { var n = te(t, "hx-" + r); a = true; i.path = n; i.verb = r; e.forEach(function (e) { Lt(t, e, i, function (e, t) { if (v(e, Q.config.disableSelector)) { m(e); return } he(r, n, e, t) }) }) } }); return a } function Lt(n, e, t, r) { if (e.sseEvent) { Rt(n, r, e.sseEvent) } else if (e.trigger === "revealed") { gt(); ht(n, r, t, e); pt(n) } else if (e.trigger === "intersect") { var i = {}; if (e.root) { i.root = ue(n, e.root) } if (e.threshold) { i.threshold = parseFloat(e.threshold) } var a = new IntersectionObserver(function (e) { for (var t = 0; t < e.length; t++) { var r = e[t]; if (r.isIntersecting) { ce(n, "intersect"); break } } }, i); a.observe(n); ht(n, r, t, e) } else if (e.trigger === "load") { if (!ct(e, n, Wt("load", { elt: n }))) { qt(n, r, t, e.delay) } } else if (e.pollInterval > 0) { t.polling = true; ot(n, r, e) } else { ht(n, r, t, e) } } function At(e) { if (!e.htmxExecuted && Q.config.allowScriptTags && (e.type === "text/javascript" || e.type === "module" || e.type === "")) { var t = re().createElement("script"); oe(e.attributes, function (e) { t.setAttribute(e.name, e.value) }); t.textContent = e.textContent; t.async = false; if (Q.config.inlineScriptNonce) { t.nonce = Q.config.inlineScriptNonce } var r = e.parentElement; try { r.insertBefore(t, e) } catch (e) { b(e) } finally { if (e.parentElement) { e.parentElement.removeChild(e) } } } } function Nt(e) { if (h(e, "script")) { At(e) } oe(f(e, "script"), function (e) { At(e) }) } function It(e) { var t = e.attributes; if (!t) { return false } for (var r = 0; r < t.length; r++) { var n = t[r].name; if (g(n, "hx-on:") || g(n, "data-hx-on:") || g(n, "hx-on-") || g(n, "data-hx-on-")) { return true } } return false } function kt(e) { var t = null; var r = []; if (It(e)) { r.push(e) } if (document.evaluate) { var n = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' + ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', e); while (t = n.iterateNext()) r.push(t) } else if (typeof e.getElementsByTagName === "function") { var i = e.getElementsByTagName("*"); for (var a = 0; a < i.length; a++) { if (It(i[a])) { r.push(i[a]) } } } return r } function Pt(e) { if (e.querySelectorAll) { var t = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; var r = e.querySelectorAll(i + t + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," + " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]"); return r } else { return [] } } function Mt(e) { var t = v(e.target, "button, input[type='submit']"); var r = Dt(e); if (r) { r.lastButtonClicked = t } } function Xt(e) { var t = Dt(e); if (t) { t.lastButtonClicked = null } } function Dt(e) { var t = v(e.target, "button, input[type='submit']"); if (!t) { return } var r = p("#" + ee(t, "form")) || v(t, "form"); if (!r) { return } return ae(r) } function Ut(e) { e.addEventListener("click", Mt); e.addEventListener("focusin", Mt); e.addEventListener("focusout", Xt) } function Bt(e) { var t = Ye(e); var r = 0; for (var n = 0; n < t.length; n++) { const i = t[n]; if (i === "{") { r++ } else if (i === "}") { r-- } } return r } function Ft(t, e, r) { var n = ae(t); if (!Array.isArray(n.onHandlers)) { n.onHandlers = [] } var i; var a = function (e) { return Tr(t, function () { if (!i) { i = new Function("event", r) } i.call(t, e) }) }; t.addEventListener(e, a); n.onHandlers.push({ event: e, listener: a }) } function Vt(e) { var t = te(e, "hx-on"); if (t) { var r = {}; var n = t.split("\n"); var i = null; var a = 0; while (n.length > 0) { var o = n.shift(); var s = o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/); if (a === 0 && s) { o.split(":"); i = s[1].slice(0, -1); r[i] = s[2] } else { r[i] += o } a += Bt(o) } for (var l in r) { Ft(e, l, r[l]) } } } function jt(e) { Ae(e); for (var t = 0; t < e.attributes.length; t++) { var r = e.attributes[t].name; var n = e.attributes[t].value; if (g(r, "hx-on") || g(r, "data-hx-on")) { var i = r.indexOf("-on") + 3; var a = r.slice(i, i + 1); if (a === "-" || a === ":") { var o = r.slice(i + 1); if (g(o, ":")) { o = "htmx" + o } else if (g(o, "-")) { o = "htmx:" + o.slice(1) } else if (g(o, "htmx-")) { o = "htmx:" + o.slice(5) } Ft(e, o, n) } } } } function _t(t) { if (v(t, Q.config.disableSelector)) { m(t); return } var r = ae(t); if (r.initHash !== Le(t)) { Ne(t); r.initHash = Le(t); Vt(t); ce(t, "htmx:beforeProcessNode"); if (t.value) { r.lastValue = t.value } var e = it(t); var n = Ht(t, r, e); if (!n) { if (ne(t, "hx-boost") === "true") { lt(t, r, e) } else if (o(t, "hx-trigger")) { e.forEach(function (e) { Lt(t, e, r, function () { }) }) } } if (t.tagName === "FORM" || ee(t, "type") === "submit" && o(t, "form")) { Ut(t) } var i = te(t, "hx-sse"); if (i) { St(t, r, i) } var a = te(t, "hx-ws"); if (a) { mt(t, r, a) } ce(t, "htmx:afterProcessNode") } } function zt(e) { e = p(e); if (v(e, Q.config.disableSelector)) { m(e); return } _t(e); oe(Pt(e), function (e) { _t(e) }); oe(kt(e), jt) } function $t(e) { return e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase() } function Wt(e, t) { var r; if (window.CustomEvent && typeof window.CustomEvent === "function") { r = new CustomEvent(e, { bubbles: true, cancelable: true, detail: t }) } else { r = re().createEvent("CustomEvent"); r.initCustomEvent(e, true, true, t) } return r } function fe(e, t, r) { ce(e, t, le({ error: t }, r)) } function Gt(e) { return e === "htmx:afterProcessNode" } function R(e, t) { oe(Fr(e), function (e) { try { t(e) } catch (e) { b(e) } }) } function b(e) { if (console.error) { console.error(e) } else if (console.log) { console.log("ERROR: ", e) } } function ce(e, t, r) { e = p(e); if (r == null) { r = {} } r["elt"] = e; var n = Wt(t, r); if (Q.logger && !Gt(t)) { Q.logger(e, t, r) } if (r.error) { b(r.error); ce(e, "htmx:error", { errorInfo: r }) } var i = e.dispatchEvent(n); var a = $t(t); if (i && a !== t) { var o = Wt(a, n.detail); i = i && e.dispatchEvent(o) } R(e, function (e) { i = i && (e.onEvent(t, n) !== false && !n.defaultPrevented) }); return i } var Jt = location.pathname + location.search; function Zt() { var e = re().querySelector("[hx-history-elt],[data-hx-history-elt]"); return e || re().body } function Kt(e, t, r, n) { if (!U()) { return } if (Q.config.historyCacheSize <= 0) { localStorage.removeItem("htmx-history-cache"); return } e = B(e); var i = E(localStorage.getItem("htmx-history-cache")) || []; for (var a = 0; a < i.length; a++) { if (i[a].url === e) { i.splice(a, 1); break } } var o = { url: e, content: t, title: r, scroll: n }; ce(re().body, "htmx:historyItemCreated", { item: o, cache: i }); i.push(o); while (i.length > Q.config.historyCacheSize) { i.shift() } while (i.length > 0) { try { localStorage.setItem("htmx-history-cache", JSON.stringify(i)); break } catch (e) { fe(re().body, "htmx:historyCacheError", { cause: e, cache: i }); i.shift() } } } function Yt(e) { if (!U()) { return null } e = B(e); var t = E(localStorage.getItem("htmx-history-cache")) || []; for (var r = 0; r < t.length; r++) { if (t[r].url === e) { return t[r] } } return null } function Qt(e) { var t = Q.config.requestClass; var r = e.cloneNode(true); oe(f(r, "." + t), function (e) { n(e, t) }); return r.innerHTML } function er() { var e = Zt(); var t = Jt || location.pathname + location.search; var r; try { r = re().querySelector('[hx-history="false" i],[data-hx-history="false" i]') } catch (e) { r = re().querySelector('[hx-history="false"],[data-hx-history="false"]') } if (!r) { ce(re().body, "htmx:beforeHistorySave", { path: t, historyElt: e }); Kt(t, Qt(e), re().title, window.scrollY) } if (Q.config.historyEnabled) history.replaceState({ htmx: true }, re().title, window.location.href) } function tr(e) { if (Q.config.getCacheBusterParam) { e = e.replace(/org\.htmx\.cache-buster=[^&]*&?/, ""); if (G(e, "&") || G(e, "?")) { e = e.slice(0, -1) } } if (Q.config.historyEnabled) { history.pushState({ htmx: true }, "", e) } Jt = e } function rr(e) { if (Q.config.historyEnabled) history.replaceState({ htmx: true }, "", e); Jt = e } function nr(e) { oe(e, function (e) { e.call() }) } function ir(a) { var e = new XMLHttpRequest; var o = { path: a, xhr: e }; ce(re().body, "htmx:historyCacheMiss", o); e.open("GET", a, true); e.setRequestHeader("HX-Request", "true"); e.setRequestHeader("HX-History-Restore-Request", "true"); e.setRequestHeader("HX-Current-URL", re().location.href); e.onload = function () { if (this.status >= 200 && this.status < 400) { ce(re().body, "htmx:historyCacheMissLoad", o); var e = l(this.response); e = e.querySelector("[hx-history-elt],[data-hx-history-elt]") || e; var t = Zt(); var r = T(t); var n = Ve(this.response); if (n) { var i = C("title"); if (i) { i.innerHTML = n } else { window.document.title = n } } Ue(t, e, r); nr(r.tasks); Jt = a; ce(re().body, "htmx:historyRestore", { path: a, cacheMiss: true, serverResponse: this.response }) } else { fe(re().body, "htmx:historyCacheMissLoadError", o) } }; e.send() } function ar(e) { er(); e = e || location.pathname + location.search; var t = Yt(e); if (t) { var r = l(t.content); var n = Zt(); var i = T(n); Ue(n, r, i); nr(i.tasks); document.title = t.title; setTimeout(function () { window.scrollTo(0, t.scroll) }, 0); Jt = e; ce(re().body, "htmx:historyRestore", { path: e, item: t }) } else { if (Q.config.refreshOnHistoryMiss) { window.location.reload(true) } else { ir(e) } } } function or(e) { var t = me(e, "hx-indicator"); if (t == null) { t = [e] } oe(t, function (e) { var t = ae(e); t.requestCount = (t.requestCount || 0) + 1; e.classList["add"].call(e.classList, Q.config.requestClass) }); return t } function sr(e) { var t = me(e, "hx-disabled-elt"); if (t == null) { t = [] } oe(t, function (e) { var t = ae(e); t.requestCount = (t.requestCount || 0) + 1; e.setAttribute("disabled", "") }); return t } function lr(e, t) { oe(e, function (e) { var t = ae(e); t.requestCount = (t.requestCount || 0) - 1; if (t.requestCount === 0) { e.classList["remove"].call(e.classList, Q.config.requestClass) } }); oe(t, function (e) { var t = ae(e); t.requestCount = (t.requestCount || 0) - 1; if (t.requestCount === 0) { e.removeAttribute("disabled") } }) } function ur(e, t) { for (var r = 0; r < e.length; r++) { var n = e[r]; if (n.isSameNode(t)) { return true } } return false } function fr(e) { if (e.name === "" || e.name == null || e.disabled || v(e, "fieldset[disabled]")) { return false } if (e.type === "button" || e.type === "submit" || e.tagName === "image" || e.tagName === "reset" || e.tagName === "file") { return false } if (e.type === "checkbox" || e.type === "radio") { return e.checked } return true } function cr(e, t, r) { if (e != null && t != null) { var n = r[e]; if (n === undefined) { r[e] = t } else if (Array.isArray(n)) { if (Array.isArray(t)) { r[e] = n.concat(t) } else { n.push(t) } } else { if (Array.isArray(t)) { r[e] = [n].concat(t) } else { r[e] = [n, t] } } } } function hr(t, r, n, e, i) { if (e == null || ur(t, e)) { return } else { t.push(e) } if (fr(e)) { var a = ee(e, "name"); var o = e.value; if (e.multiple && e.tagName === "SELECT") { o = M(e.querySelectorAll("option:checked")).map(function (e) { return e.value }) } if (e.files) { o = M(e.files) } cr(a, o, r); if (i) { vr(e, n) } } if (h(e, "form")) { var s = e.elements; oe(s, function (e) { hr(t, r, n, e, i) }) } } function vr(e, t) { if (e.willValidate) { ce(e, "htmx:validation:validate"); if (!e.checkValidity()) { t.push({ elt: e, message: e.validationMessage, validity: e.validity }); ce(e, "htmx:validation:failed", { message: e.validationMessage, validity: e.validity }) } } } function dr(e, t) { var r = []; var n = {}; var i = {}; var a = []; var o = ae(e); if (o.lastButtonClicked && !se(o.lastButtonClicked)) { o.lastButtonClicked = null } var s = h(e, "form") && e.noValidate !== true || te(e, "hx-validate") === "true"; if (o.lastButtonClicked) { s = s && o.lastButtonClicked.formNoValidate !== true } if (t !== "get") { hr(r, i, a, v(e, "form"), s) } hr(r, n, a, e, s); if (o.lastButtonClicked || e.tagName === "BUTTON" || e.tagName === "INPUT" && ee(e, "type") === "submit") { var l = o.lastButtonClicked || e; var u = ee(l, "name"); cr(u, l.value, i) } var f = me(e, "hx-include"); oe(f, function (e) { hr(r, n, a, e, s); if (!h(e, "form")) { oe(e.querySelectorAll(rt), function (e) { hr(r, n, a, e, s) }) } }); n = le(n, i); return { errors: a, values: n } } function gr(e, t, r) { if (e !== "") { e += "&" } if (String(r) === "[object Object]") { r = JSON.stringify(r) } var n = encodeURIComponent(r); e += encodeURIComponent(t) + "=" + n; return e } function pr(e) { var t = ""; for (var r in e) { if (e.hasOwnProperty(r)) { var n = e[r]; if (Array.isArray(n)) { oe(n, function (e) { t = gr(t, r, e) }) } else { t = gr(t, r, n) } } } return t } function mr(e) { var t = new FormData; for (var r in e) { if (e.hasOwnProperty(r)) { var n = e[r]; if (Array.isArray(n)) { oe(n, function (e) { t.append(r, e) }) } else { t.append(r, n) } } } return t } function xr(e, t, r) { var n = { "HX-Request": "true", "HX-Trigger": ee(e, "id"), "HX-Trigger-Name": ee(e, "name"), "HX-Target": te(t, "id"), "HX-Current-URL": re().location.href }; Rr(e, "hx-headers", false, n); if (r !== undefined) { n["HX-Prompt"] = r } if (ae(e).boosted) { n["HX-Boosted"] = "true" } return n } function yr(t, e) { var r = ne(e, "hx-params"); if (r) { if (r === "none") { return {} } else if (r === "*") { return t } else if (r.indexOf("not ") === 0) { oe(r.substr(4).split(","), function (e) { e = e.trim(); delete t[e] }); return t } else { var n = {}; oe(r.split(","), function (e) { e = e.trim(); n[e] = t[e] }); return n } } else { return t } } function br(e) { return ee(e, "href") && ee(e, "href").indexOf("#") >= 0 } function wr(e, t) { var r = t ? t : ne(e, "hx-swap"); var n = { swapStyle: ae(e).boosted ? "innerHTML" : Q.config.defaultSwapStyle, swapDelay: Q.config.defaultSwapDelay, settleDelay: Q.config.defaultSettleDelay }; if (Q.config.scrollIntoViewOnBoost && ae(e).boosted && !br(e)) { n["show"] = "top" } if (r) { var i = D(r); if (i.length > 0) { for (var a = 0; a < i.length; a++) { var o = i[a]; if (o.indexOf("swap:") === 0) { n["swapDelay"] = d(o.substr(5)) } else if (o.indexOf("settle:") === 0) { n["settleDelay"] = d(o.substr(7)) } else if (o.indexOf("transition:") === 0) { n["transition"] = o.substr(11) === "true" } else if (o.indexOf("ignoreTitle:") === 0) { n["ignoreTitle"] = o.substr(12) === "true" } else if (o.indexOf("scroll:") === 0) { var s = o.substr(7); var l = s.split(":"); var u = l.pop(); var f = l.length > 0 ? l.join(":") : null; n["scroll"] = u; n["scrollTarget"] = f } else if (o.indexOf("show:") === 0) { var c = o.substr(5); var l = c.split(":"); var h = l.pop(); var f = l.length > 0 ? l.join(":") : null; n["show"] = h; n["showTarget"] = f } else if (o.indexOf("focus-scroll:") === 0) { var v = o.substr("focus-scroll:".length); n["focusScroll"] = v == "true" } else if (a == 0) { n["swapStyle"] = o } else { b("Unknown modifier in hx-swap: " + o) } } } } return n } function Sr(e) { return ne(e, "hx-encoding") === "multipart/form-data" || h(e, "form") && ee(e, "enctype") === "multipart/form-data" } function Er(t, r, n) { var i = null; R(r, function (e) { if (i == null) { i = e.encodeParameters(t, n, r) } }); if (i != null) { return i } else { if (Sr(r)) { return mr(n) } else { return pr(n) } } } function T(e) { return { tasks: [], elts: [e] } } function Cr(e, t) { var r = e[0]; var n = e[e.length - 1]; if (t.scroll) { var i = null; if (t.scrollTarget) { i = ue(r, t.scrollTarget) } if (t.scroll === "top" && (r || i)) { i = i || r; i.scrollTop = 0 } if (t.scroll === "bottom" && (n || i)) { i = i || n; i.scrollTop = i.scrollHeight } } if (t.show) { var i = null; if (t.showTarget) { var a = t.showTarget; if (t.showTarget === "window") { a = "body" } i = ue(r, a) } if (t.show === "top" && (r || i)) { i = i || r; i.scrollIntoView({ block: "start", behavior: Q.config.scrollBehavior }) } if (t.show === "bottom" && (n || i)) { i = i || n; i.scrollIntoView({ block: "end", behavior: Q.config.scrollBehavior }) } } } function Rr(e, t, r, n) { if (n == null) { n = {} } if (e == null) { return n } var i = te(e, t); if (i) { var a = i.trim(); var o = r; if (a === "unset") { return null } if (a.indexOf("javascript:") === 0) { a = a.substr(11); o = true } else if (a.indexOf("js:") === 0) { a = a.substr(3); o = true } if (a.indexOf("{") !== 0) { a = "{" + a + "}" } var s; if (o) { s = Tr(e, function () { return Function("return (" + a + ")")() }, {}) } else { s = E(a) } for (var l in s) { if (s.hasOwnProperty(l)) { if (n[l] == null) { n[l] = s[l] } } } } return Rr(u(e), t, r, n) } function Tr(e, t, r) { if (Q.config.allowEval) { return t() } else { fe(e, "htmx:evalDisallowedError"); return r } } function Or(e, t) { return Rr(e, "hx-vars", true, t) } function qr(e, t) { return Rr(e, "hx-vals", false, t) } function Hr(e) { return le(Or(e), qr(e)) } function Lr(t, r, n) { if (n !== null) { try { t.setRequestHeader(r, n) } catch (e) { t.setRequestHeader(r, encodeURIComponent(n)); t.setRequestHeader(r + "-URI-AutoEncoded", "true") } } } function Ar(t) { if (t.responseURL && typeof URL !== "undefined") { try { var e = new URL(t.responseURL); return e.pathname + e.search } catch (e) { fe(re().body, "htmx:badResponseUrl", { url: t.responseURL }) } } } function O(e, t) { return t.test(e.getAllResponseHeaders()) } function Nr(e, t, r) { e = e.toLowerCase(); if (r) { if (r instanceof Element || I(r, "String")) { return he(e, t, null, null, { targetOverride: p(r), returnPromise: true }) } else { return he(e, t, p(r.source), r.event, { handler: r.handler, headers: r.headers, values: r.values, targetOverride: p(r.target), swapOverride: r.swap, select: r.select, returnPromise: true }) } } else { return he(e, t, null, null, { returnPromise: true }) } } function Ir(e) { var t = []; while (e) { t.push(e); e = e.parentElement } return t } function kr(e, t, r) { var n; var i; if (typeof URL === "function") { i = new URL(t, document.location.href); var a = document.location.origin; n = a === i.origin } else { i = t; n = g(t, document.location.origin) } if (Q.config.selfRequestsOnly) { if (!n) { return false } } return ce(e, "htmx:validateUrl", le({ url: i, sameHost: n }, r)) } function he(t, r, n, i, a, e) { var o = null; var s = null; a = a != null ? a : {}; if (a.returnPromise && typeof Promise !== "undefined") { var l = new Promise(function (e, t) { o = e; s = t }) } if (n == null) { n = re().body } var M = a.handler || Mr; var X = a.select || null; if (!se(n)) { ie(o); return l } var u = a.targetOverride || ye(n); if (u == null || u == pe) { fe(n, "htmx:targetError", { target: te(n, "hx-target") }); ie(s); return l } var f = ae(n); var c = f.lastButtonClicked; if (c) { var h = ee(c, "formaction"); if (h != null) { r = h } var v = ee(c, "formmethod"); if (v != null) { if (v.toLowerCase() !== "dialog") { t = v } } } var d = ne(n, "hx-confirm"); if (e === undefined) { var D = function (e) { return he(t, r, n, i, a, !!e) }; var U = { target: u, elt: n, path: r, verb: t, triggeringEvent: i, etc: a, issueRequest: D, question: d }; if (ce(n, "htmx:confirm", U) === false) { ie(o); return l } } var g = n; var p = ne(n, "hx-sync"); var m = null; var x = false; if (p) { var B = p.split(":"); var F = B[0].trim(); if (F === "this") { g = xe(n, "hx-sync") } else { g = ue(n, F) } p = (B[1] || "drop").trim(); f = ae(g); if (p === "drop" && f.xhr && f.abortable !== true) { ie(o); return l } else if (p === "abort") { if (f.xhr) { ie(o); return l } else { x = true } } else if (p === "replace") { ce(g, "htmx:abort") } else if (p.indexOf("queue") === 0) { var V = p.split(" "); m = (V[1] || "last").trim() } } if (f.xhr) { if (f.abortable) { ce(g, "htmx:abort") } else { if (m == null) { if (i) { var y = ae(i); if (y && y.triggerSpec && y.triggerSpec.queue) { m = y.triggerSpec.queue } } if (m == null) { m = "last" } } if (f.queuedRequests == null) { f.queuedRequests = [] } if (m === "first" && f.queuedRequests.length === 0) { f.queuedRequests.push(function () { he(t, r, n, i, a) }) } else if (m === "all") { f.queuedRequests.push(function () { he(t, r, n, i, a) }) } else if (m === "last") { f.queuedRequests = []; f.queuedRequests.push(function () { he(t, r, n, i, a) }) } ie(o); return l } } var b = new XMLHttpRequest; f.xhr = b; f.abortable = x; var w = function () { f.xhr = null; f.abortable = false; if (f.queuedRequests != null && f.queuedRequests.length > 0) { var e = f.queuedRequests.shift(); e() } }; var j = ne(n, "hx-prompt"); if (j) { var S = prompt(j); if (S === null || !ce(n, "htmx:prompt", { prompt: S, target: u })) { ie(o); w(); return l } } if (d && !e) { if (!confirm(d)) { ie(o); w(); return l } } var E = xr(n, u, S); if (t !== "get" && !Sr(n)) { E["Content-Type"] = "application/x-www-form-urlencoded" } if (a.headers) { E = le(E, a.headers) } var _ = dr(n, t); var C = _.errors; var R = _.values; if (a.values) { R = le(R, a.values) } var z = Hr(n); var $ = le(R, z); var T = yr($, n); if (Q.config.getCacheBusterParam && t === "get") { T["org.htmx.cache-buster"] = ee(u, "id") || "true" } if (r == null || r === "") { r = re().location.href } var O = Rr(n, "hx-request"); var W = ae(n).boosted; var q = Q.config.methodsThatUseUrlParams.indexOf(t) >= 0; var H = { boosted: W, useUrlParams: q, parameters: T, unfilteredParameters: $, headers: E, target: u, verb: t, errors: C, withCredentials: a.credentials || O.credentials || Q.config.withCredentials, timeout: a.timeout || O.timeout || Q.config.timeout, path: r, triggeringEvent: i }; if (!ce(n, "htmx:configRequest", H)) { ie(o); w(); return l } r = H.path; t = H.verb; E = H.headers; T = H.parameters; C = H.errors; q = H.useUrlParams; if (C && C.length > 0) { ce(n, "htmx:validation:halted", H); ie(o); w(); return l } var G = r.split("#"); var J = G[0]; var L = G[1]; var A = r; if (q) { A = J; var Z = Object.keys(T).length !== 0; if (Z) { if (A.indexOf("?") < 0) { A += "?" } else { A += "&" } A += pr(T); if (L) { A += "#" + L } } } if (!kr(n, A, H)) { fe(n, "htmx:invalidPath", H); ie(s); return l } b.open(t.toUpperCase(), A, true); b.overrideMimeType("text/html"); b.withCredentials = H.withCredentials; b.timeout = H.timeout; if (O.noHeaders) { } else { for (var N in E) { if (E.hasOwnProperty(N)) { var K = E[N]; Lr(b, N, K) } } } var I = { xhr: b, target: u, requestConfig: H, etc: a, boosted: W, select: X, pathInfo: { requestPath: r, finalRequestPath: A, anchor: L } }; b.onload = function () { try { var e = Ir(n); I.pathInfo.responsePath = Ar(b); M(n, I); lr(k, P); ce(n, "htmx:afterRequest", I); ce(n, "htmx:afterOnLoad", I); if (!se(n)) { var t = null; while (e.length > 0 && t == null) { var r = e.shift(); if (se(r)) { t = r } } if (t) { ce(t, "htmx:afterRequest", I); ce(t, "htmx:afterOnLoad", I) } } ie(o); w() } catch (e) { fe(n, "htmx:onLoadError", le({ error: e }, I)); throw e } }; b.onerror = function () { lr(k, P); fe(n, "htmx:afterRequest", I); fe(n, "htmx:sendError", I); ie(s); w() }; b.onabort = function () { lr(k, P); fe(n, "htmx:afterRequest", I); fe(n, "htmx:sendAbort", I); ie(s); w() }; b.ontimeout = function () { lr(k, P); fe(n, "htmx:afterRequest", I); fe(n, "htmx:timeout", I); ie(s); w() }; if (!ce(n, "htmx:beforeRequest", I)) { ie(o); w(); return l } var k = or(n); var P = sr(n); oe(["loadstart", "loadend", "progress", "abort"], function (t) { oe([b, b.upload], function (e) { e.addEventListener(t, function (e) { ce(n, "htmx:xhr:" + t, { lengthComputable: e.lengthComputable, loaded: e.loaded, total: e.total }) }) }) }); ce(n, "htmx:beforeSend", I); var Y = q ? null : Er(b, n, T); b.send(Y); return l } function Pr(e, t) { var r = t.xhr; var n = null; var i = null; if (O(r, /HX-Push:/i)) { n = r.getResponseHeader("HX-Push"); i = "push" } else if (O(r, /HX-Push-Url:/i)) { n = r.getResponseHeader("HX-Push-Url"); i = "push" } else if (O(r, /HX-Replace-Url:/i)) { n = r.getResponseHeader("HX-Replace-Url"); i = "replace" } if (n) { if (n === "false") { return {} } else { return { type: i, path: n } } } var a = t.pathInfo.finalRequestPath; var o = t.pathInfo.responsePath; var s = ne(e, "hx-push-url"); var l = ne(e, "hx-replace-url"); var u = ae(e).boosted; var f = null; var c = null; if (s) { f = "push"; c = s } else if (l) { f = "replace"; c = l } else if (u) { f = "push"; c = o || a } if (c) { if (c === "false") { return {} } if (c === "true") { c = o || a } if (t.pathInfo.anchor && c.indexOf("#") === -1) { c = c + "#" + t.pathInfo.anchor } return { type: f, path: c } } else { return {} } } function Mr(l, u) { var f = u.xhr; var c = u.target; var e = u.etc; var t = u.requestConfig; var h = u.select; if (!ce(l, "htmx:beforeOnLoad", u)) return; if (O(f, /HX-Trigger:/i)) { _e(f, "HX-Trigger", l) } if (O(f, /HX-Location:/i)) { er(); var r = f.getResponseHeader("HX-Location"); var v; if (r.indexOf("{") === 0) { v = E(r); r = v["path"]; delete v["path"] } Nr("GET", r, v).then(function () { tr(r) }); return } var n = O(f, /HX-Refresh:/i) && "true" === f.getResponseHeader("HX-Refresh"); if (O(f, /HX-Redirect:/i)) { location.href = f.getResponseHeader("HX-Redirect"); n && location.reload(); return } if (n) { location.reload(); return } if (O(f, /HX-Retarget:/i)) { if (f.getResponseHeader("HX-Retarget") === "this") { u.target = l } else { u.target = ue(l, f.getResponseHeader("HX-Retarget")) } } var d = Pr(l, u); var i = f.status >= 200 && f.status < 400 && f.status !== 204; var g = f.response; var a = f.status >= 400; var p = Q.config.ignoreTitle; var o = le({ shouldSwap: i, serverResponse: g, isError: a, ignoreTitle: p }, u); if (!ce(c, "htmx:beforeSwap", o)) return; c = o.target; g = o.serverResponse; a = o.isError; p = o.ignoreTitle; u.target = c; u.failed = a; u.successful = !a; if (o.shouldSwap) { if (f.status === 286) { at(l) } R(l, function (e) { g = e.transformResponse(g, f, l) }); if (d.type) { er() } var s = e.swapOverride; if (O(f, /HX-Reswap:/i)) { s = f.getResponseHeader("HX-Reswap") } var v = wr(l, s); if (v.hasOwnProperty("ignoreTitle")) { p = v.ignoreTitle } c.classList.add(Q.config.swappingClass); var m = null; var x = null; var y = function () { try { var e = document.activeElement; var t = {}; try { t = { elt: e, start: e ? e.selectionStart : null, end: e ? e.selectionEnd : null } } catch (e) { } var r; if (h) { r = h } if (O(f, /HX-Reselect:/i)) { r = f.getResponseHeader("HX-Reselect") } if (d.type) { ce(re().body, "htmx:beforeHistoryUpdate", le({ history: d }, u)); if (d.type === "push") { tr(d.path); ce(re().body, "htmx:pushedIntoHistory", { path: d.path }) } else { rr(d.path); ce(re().body, "htmx:replacedInHistory", { path: d.path }) } } var n = T(c); je(v.swapStyle, c, l, g, n, r); if (t.elt && !se(t.elt) && ee(t.elt, "id")) { var i = document.getElementById(ee(t.elt, "id")); var a = { preventScroll: v.focusScroll !== undefined ? !v.focusScroll : !Q.config.defaultFocusScroll }; if (i) { if (t.start && i.setSelectionRange) { try { i.setSelectionRange(t.start, t.end) } catch (e) { } } i.focus(a) } } c.classList.remove(Q.config.swappingClass); oe(n.elts, function (e) { if (e.classList) { e.classList.add(Q.config.settlingClass) } ce(e, "htmx:afterSwap", u) }); if (O(f, /HX-Trigger-After-Swap:/i)) { var o = l; if (!se(l)) { o = re().body } _e(f, "HX-Trigger-After-Swap", o) } var s = function () { oe(n.tasks, function (e) { e.call() }); oe(n.elts, function (e) { if (e.classList) { e.classList.remove(Q.config.settlingClass) } ce(e, "htmx:afterSettle", u) }); if (u.pathInfo.anchor) { var e = re().getElementById(u.pathInfo.anchor); if (e) { e.scrollIntoView({ block: "start", behavior: "auto" }) } } if (n.title && !p) { var t = C("title"); if (t) { t.innerHTML = n.title } else { window.document.title = n.title } } Cr(n.elts, v); if (O(f, /HX-Trigger-After-Settle:/i)) { var r = l; if (!se(l)) { r = re().body } _e(f, "HX-Trigger-After-Settle", r) } ie(m) }; if (v.settleDelay > 0) { setTimeout(s, v.settleDelay) } else { s() } } catch (e) { fe(l, "htmx:swapError", u); ie(x); throw e } }; var b = Q.config.globalViewTransitions; if (v.hasOwnProperty("transition")) { b = v.transition } if (b && ce(l, "htmx:beforeTransition", u) && typeof Promise !== "undefined" && document.startViewTransition) { var w = new Promise(function (e, t) { m = e; x = t }); var S = y; y = function () { document.startViewTransition(function () { S(); return w }) } } if (v.swapDelay > 0) { setTimeout(y, v.swapDelay) } else { y() } } if (a) { fe(l, "htmx:responseError", le({ error: "Response Status Error Code " + f.status + " from " + u.pathInfo.requestPath }, u)) } } var Xr = {}; function Dr() { return { init: function (e) { return null }, onEvent: function (e, t) { return true }, transformResponse: function (e, t, r) { return e }, isInlineSwap: function (e) { return false }, handleSwap: function (e, t, r, n) { return false }, encodeParameters: function (e, t, r) { return null } } } function Ur(e, t) { if (t.init) { t.init(r) } Xr[e] = le(Dr(), t) } function Br(e) { delete Xr[e] } function Fr(e, r, n) { if (e == undefined) { return r } if (r == undefined) { r = [] } if (n == undefined) { n = [] } var t = te(e, "hx-ext"); if (t) { oe(t.split(","), function (e) { e = e.replace(/ /g, ""); if (e.slice(0, 7) == "ignore:") { n.push(e.slice(7)); return } if (n.indexOf(e) < 0) { var t = Xr[e]; if (t && r.indexOf(t) < 0) { r.push(t) } } }) } return Fr(u(e), r, n) } var Vr = false; re().addEventListener("DOMContentLoaded", function () { Vr = true }); function jr(e) { if (Vr || re().readyState === "complete") { e() } else { re().addEventListener("DOMContentLoaded", e) } } function _r() { if (Q.config.includeIndicatorStyles !== false) { re().head.insertAdjacentHTML("beforeend", "") } } function zr() { var e = re().querySelector('meta[name="htmx-config"]'); if (e) { return E(e.content) } else { return null } } function $r() { var e = zr(); if (e) { Q.config = le(Q.config, e) } } jr(function () { $r(); _r(); var e = re().body; zt(e); var t = re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']"); e.addEventListener("htmx:abort", function (e) { var t = e.target; var r = ae(t); if (r && r.xhr) { r.xhr.abort() } }); const r = window.onpopstate ? window.onpopstate.bind(window) : null; window.onpopstate = function (e) { if (e.state && e.state.htmx) { ar(); oe(t, function (e) { ce(e, "htmx:restored", { document: re(), triggerEvent: ce }) }) } else { if (r) { r(e) } } }; setTimeout(function () { ce(e, "htmx:load", {}); e = null }, 0) }); return Q }() }); diff --git a/tad/templates/layout.html b/tad/templates/layout.html index 1f613521..20e74108 100644 --- a/tad/templates/layout.html +++ b/tad/templates/layout.html @@ -8,6 +8,8 @@ + + {% block head %}{% endblock %} From f2da393fb2cfd5705a8d4e556c673c520909561d Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 11:06:06 +0000 Subject: [PATCH 46/55] Fix typo for migrations in app database filename --- tad/migrations/env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tad/migrations/env.py b/tad/migrations/env.py index d5bc0598..f790750b 100644 --- a/tad/migrations/env.py +++ b/tad/migrations/env.py @@ -18,7 +18,7 @@ def get_url(): scheme = os.getenv("APP_DATABASE_SCHEME", "sqlite") if scheme == "sqlite": - file = os.getenv("APP_DATABASE__FILE", "/database") + file = os.getenv("APP_DATABASE_FILE", "database.sqlite3") return f"{scheme}:///{file}" user = os.getenv("APP_DATABASE_USER", "tad") From b8d5ed42061318c91ff685c2297f842de75eacb4 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 11:06:36 +0000 Subject: [PATCH 47/55] Fix ci workflow for dockerfile location --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5d8c48d..da01aaa0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -172,7 +172,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: ./tad/Dockerfile + file: ./Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -182,7 +182,7 @@ jobs: - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: - image-ref: ${{ steps.meta.outputs.tags }} + image-ref: ${{ steps.meta.outputs.tags[0] }} trivy-config: trivy.yaml scan-type: image exit-code: 0 From 6e8983457103d24c4132a9d53fde2bfbf497687c Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 11:07:37 +0000 Subject: [PATCH 48/55] Update dependencies --- .pre-commit-config.yaml | 2 +- poetry.lock | 312 +++++++++++++++++++--------------------- 2 files changed, 149 insertions(+), 165 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4290a1e6..18f1b71f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: check-toml - id: detect-private-key - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.3 + rev: v0.4.4 hooks: - id: ruff - id: ruff-format diff --git a/poetry.lock b/poetry.lock index f35389eb..a45cbbf9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -695,165 +695,149 @@ testing = ["black", "isort", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)", "twin [[package]] name = "lxml" -version = "5.2.1" +version = "5.2.2" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1f7785f4f789fdb522729ae465adcaa099e2a3441519df750ebdccc481d961a1"}, - {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cc6ee342fb7fa2471bd9b6d6fdfc78925a697bf5c2bcd0a302e98b0d35bfad3"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:794f04eec78f1d0e35d9e0c36cbbb22e42d370dda1609fb03bcd7aeb458c6377"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817d420c60a5183953c783b0547d9eb43b7b344a2c46f69513d5952a78cddf3"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2213afee476546a7f37c7a9b4ad4d74b1e112a6fafffc9185d6d21f043128c81"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b070bbe8d3f0f6147689bed981d19bbb33070225373338df755a46893528104a"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e02c5175f63effbd7c5e590399c118d5db6183bbfe8e0d118bdb5c2d1b48d937"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:d7520db34088c96cc0e0a3ad51a4fd5b401f279ee112aa2b7f8f976d8582606d"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:bcbf4af004f98793a95355980764b3d80d47117678118a44a80b721c9913436a"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2b44bec7adf3e9305ce6cbfa47a4395667e744097faed97abb4728748ba7d47"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1c5bb205e9212d0ebddf946bc07e73fa245c864a5f90f341d11ce7b0b854475d"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2c9d147f754b1b0e723e6afb7ba1566ecb162fe4ea657f53d2139bbf894d050a"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3545039fa4779be2df51d6395e91a810f57122290864918b172d5dc7ca5bb433"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a91481dbcddf1736c98a80b122afa0f7296eeb80b72344d7f45dc9f781551f56"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2ddfe41ddc81f29a4c44c8ce239eda5ade4e7fc305fb7311759dd6229a080052"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7baf9ffc238e4bf401299f50e971a45bfcc10a785522541a6e3179c83eabf0a"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:31e9a882013c2f6bd2f2c974241bf4ba68c85eba943648ce88936d23209a2e01"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0a15438253b34e6362b2dc41475e7f80de76320f335e70c5528b7148cac253a1"}, - {file = "lxml-5.2.1-cp310-cp310-win32.whl", hash = "sha256:6992030d43b916407c9aa52e9673612ff39a575523c5f4cf72cdef75365709a5"}, - {file = "lxml-5.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:da052e7962ea2d5e5ef5bc0355d55007407087392cf465b7ad84ce5f3e25fe0f"}, - {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:70ac664a48aa64e5e635ae5566f5227f2ab7f66a3990d67566d9907edcbbf867"}, - {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1ae67b4e737cddc96c99461d2f75d218bdf7a0c3d3ad5604d1f5e7464a2f9ffe"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f18a5a84e16886898e51ab4b1d43acb3083c39b14c8caeb3589aabff0ee0b270"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6f2c8372b98208ce609c9e1d707f6918cc118fea4e2c754c9f0812c04ca116d"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:394ed3924d7a01b5bd9a0d9d946136e1c2f7b3dc337196d99e61740ed4bc6fe1"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d077bc40a1fe984e1a9931e801e42959a1e6598edc8a3223b061d30fbd26bbc"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:764b521b75701f60683500d8621841bec41a65eb739b8466000c6fdbc256c240"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3a6b45da02336895da82b9d472cd274b22dc27a5cea1d4b793874eead23dd14f"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:5ea7b6766ac2dfe4bcac8b8595107665a18ef01f8c8343f00710b85096d1b53a"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:e196a4ff48310ba62e53a8e0f97ca2bca83cdd2fe2934d8b5cb0df0a841b193a"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:200e63525948e325d6a13a76ba2911f927ad399ef64f57898cf7c74e69b71095"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dae0ed02f6b075426accbf6b2863c3d0a7eacc1b41fb40f2251d931e50188dad"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:ab31a88a651039a07a3ae327d68ebdd8bc589b16938c09ef3f32a4b809dc96ef"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:df2e6f546c4df14bc81f9498bbc007fbb87669f1bb707c6138878c46b06f6510"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5dd1537e7cc06efd81371f5d1a992bd5ab156b2b4f88834ca852de4a8ea523fa"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9b9ec9c9978b708d488bec36b9e4c94d88fd12ccac3e62134a9d17ddba910ea9"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8e77c69d5892cb5ba71703c4057091e31ccf534bd7f129307a4d084d90d014b8"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a8d5c70e04aac1eda5c829a26d1f75c6e5286c74743133d9f742cda8e53b9c2f"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c94e75445b00319c1fad60f3c98b09cd63fe1134a8a953dcd48989ef42318534"}, - {file = "lxml-5.2.1-cp311-cp311-win32.whl", hash = "sha256:4951e4f7a5680a2db62f7f4ab2f84617674d36d2d76a729b9a8be4b59b3659be"}, - {file = "lxml-5.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:5c670c0406bdc845b474b680b9a5456c561c65cf366f8db5a60154088c92d102"}, - {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:abc25c3cab9ec7fcd299b9bcb3b8d4a1231877e425c650fa1c7576c5107ab851"}, - {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6935bbf153f9a965f1e07c2649c0849d29832487c52bb4a5c5066031d8b44fd5"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d793bebb202a6000390a5390078e945bbb49855c29c7e4d56a85901326c3b5d9"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd5562927cdef7c4f5550374acbc117fd4ecc05b5007bdfa57cc5355864e0a4"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e7259016bc4345a31af861fdce942b77c99049d6c2107ca07dc2bba2435c1d9"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:530e7c04f72002d2f334d5257c8a51bf409db0316feee7c87e4385043be136af"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59689a75ba8d7ffca577aefd017d08d659d86ad4585ccc73e43edbfc7476781a"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f9737bf36262046213a28e789cc82d82c6ef19c85a0cf05e75c670a33342ac2c"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:3a74c4f27167cb95c1d4af1c0b59e88b7f3e0182138db2501c353555f7ec57f4"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:68a2610dbe138fa8c5826b3f6d98a7cfc29707b850ddcc3e21910a6fe51f6ca0"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f0a1bc63a465b6d72569a9bba9f2ef0334c4e03958e043da1920299100bc7c08"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c2d35a1d047efd68027817b32ab1586c1169e60ca02c65d428ae815b593e65d4"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:79bd05260359170f78b181b59ce871673ed01ba048deef4bf49a36ab3e72e80b"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:865bad62df277c04beed9478fe665b9ef63eb28fe026d5dedcb89b537d2e2ea6"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71e97313406ccf55d32cc98a533ee05c61e15d11b99215b237346171c179c0b0"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:057cdc6b86ab732cf361f8b4d8af87cf195a1f6dc5b0ff3de2dced242c2015e0"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f3bbbc998d42f8e561f347e798b85513ba4da324c2b3f9b7969e9c45b10f6169"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:491755202eb21a5e350dae00c6d9a17247769c64dcf62d8c788b5c135e179dc4"}, - {file = "lxml-5.2.1-cp312-cp312-win32.whl", hash = "sha256:8de8f9d6caa7f25b204fc861718815d41cbcf27ee8f028c89c882a0cf4ae4134"}, - {file = "lxml-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:f2a9efc53d5b714b8df2b4b3e992accf8ce5bbdfe544d74d5c6766c9e1146a3a"}, - {file = "lxml-5.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:70a9768e1b9d79edca17890175ba915654ee1725975d69ab64813dd785a2bd5c"}, - {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c38d7b9a690b090de999835f0443d8aa93ce5f2064035dfc48f27f02b4afc3d0"}, - {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5670fb70a828663cc37552a2a85bf2ac38475572b0e9b91283dc09efb52c41d1"}, - {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:958244ad566c3ffc385f47dddde4145088a0ab893504b54b52c041987a8c1863"}, - {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6241d4eee5f89453307c2f2bfa03b50362052ca0af1efecf9fef9a41a22bb4f"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2a66bf12fbd4666dd023b6f51223aed3d9f3b40fef06ce404cb75bafd3d89536"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:9123716666e25b7b71c4e1789ec829ed18663152008b58544d95b008ed9e21e9"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:0c3f67e2aeda739d1cc0b1102c9a9129f7dc83901226cc24dd72ba275ced4218"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5d5792e9b3fb8d16a19f46aa8208987cfeafe082363ee2745ea8b643d9cc5b45"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:88e22fc0a6684337d25c994381ed8a1580a6f5ebebd5ad41f89f663ff4ec2885"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:21c2e6b09565ba5b45ae161b438e033a86ad1736b8c838c766146eff8ceffff9"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_s390x.whl", hash = "sha256:afbbdb120d1e78d2ba8064a68058001b871154cc57787031b645c9142b937a62"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:627402ad8dea044dde2eccde4370560a2b750ef894c9578e1d4f8ffd54000461"}, - {file = "lxml-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:e89580a581bf478d8dcb97d9cd011d567768e8bc4095f8557b21c4d4c5fea7d0"}, - {file = "lxml-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:59565f10607c244bc4c05c0c5fa0c190c990996e0c719d05deec7030c2aa8289"}, - {file = "lxml-5.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:857500f88b17a6479202ff5fe5f580fc3404922cd02ab3716197adf1ef628029"}, - {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56c22432809085b3f3ae04e6e7bdd36883d7258fcd90e53ba7b2e463efc7a6af"}, - {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a55ee573116ba208932e2d1a037cc4b10d2c1cb264ced2184d00b18ce585b2c0"}, - {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:6cf58416653c5901e12624e4013708b6e11142956e7f35e7a83f1ab02f3fe456"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:64c2baa7774bc22dd4474248ba16fe1a7f611c13ac6123408694d4cc93d66dbd"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:74b28c6334cca4dd704e8004cba1955af0b778cf449142e581e404bd211fb619"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7221d49259aa1e5a8f00d3d28b1e0b76031655ca74bb287123ef56c3db92f213"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:04ab5415bf6c86e0518d57240a96c4d1fcfc3cb370bb2ac2a732b67f579e5a04"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:6ab833e4735a7e5533711a6ea2df26459b96f9eec36d23f74cafe03631647c41"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f443cdef978430887ed55112b491f670bba6462cea7a7742ff8f14b7abb98d75"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:9e2addd2d1866fe112bc6f80117bcc6bc25191c5ed1bfbcf9f1386a884252ae8"}, - {file = "lxml-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:f51969bac61441fd31f028d7b3b45962f3ecebf691a510495e5d2cd8c8092dbd"}, - {file = "lxml-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b0b58fbfa1bf7367dde8a557994e3b1637294be6cf2169810375caf8571a085c"}, - {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:804f74efe22b6a227306dd890eecc4f8c59ff25ca35f1f14e7482bbce96ef10b"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08802f0c56ed150cc6885ae0788a321b73505d2263ee56dad84d200cab11c07a"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8c09ed18ecb4ebf23e02b8e7a22a05d6411911e6fabef3a36e4f371f4f2585"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3d30321949861404323c50aebeb1943461a67cd51d4200ab02babc58bd06a86"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:b560e3aa4b1d49e0e6c847d72665384db35b2f5d45f8e6a5c0072e0283430533"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:058a1308914f20784c9f4674036527e7c04f7be6fb60f5d61353545aa7fcb739"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:adfb84ca6b87e06bc6b146dc7da7623395db1e31621c4785ad0658c5028b37d7"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a2dfe7e2473f9b59496247aad6e23b405ddf2e12ef0765677b0081c02d6c2c0b"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bf2e2458345d9bffb0d9ec16557d8858c9c88d2d11fed53998512504cd9df49b"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:58278b29cb89f3e43ff3e0c756abbd1518f3ee6adad9e35b51fb101c1c1daaec"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:64641a6068a16201366476731301441ce93457eb8452056f570133a6ceb15fca"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:78bfa756eab503673991bdcf464917ef7845a964903d3302c5f68417ecdc948c"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11a04306fcba10cd9637e669fd73aa274c1c09ca64af79c041aa820ea992b637"}, - {file = "lxml-5.2.1-cp38-cp38-win32.whl", hash = "sha256:66bc5eb8a323ed9894f8fa0ee6cb3e3fb2403d99aee635078fd19a8bc7a5a5da"}, - {file = "lxml-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:9676bfc686fa6a3fa10cd4ae6b76cae8be26eb5ec6811d2a325636c460da1806"}, - {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cf22b41fdae514ee2f1691b6c3cdeae666d8b7fa9434de445f12bbeee0cf48dd"}, - {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec42088248c596dbd61d4ae8a5b004f97a4d91a9fd286f632e42e60b706718d7"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd53553ddad4a9c2f1f022756ae64abe16da1feb497edf4d9f87f99ec7cf86bd"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feaa45c0eae424d3e90d78823f3828e7dc42a42f21ed420db98da2c4ecf0a2cb"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddc678fb4c7e30cf830a2b5a8d869538bc55b28d6c68544d09c7d0d8f17694dc"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:853e074d4931dbcba7480d4dcab23d5c56bd9607f92825ab80ee2bd916edea53"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4691d60512798304acb9207987e7b2b7c44627ea88b9d77489bbe3e6cc3bd4"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:beb72935a941965c52990f3a32d7f07ce869fe21c6af8b34bf6a277b33a345d3"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:6588c459c5627fefa30139be4d2e28a2c2a1d0d1c265aad2ba1935a7863a4913"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:588008b8497667f1ddca7c99f2f85ce8511f8f7871b4a06ceede68ab62dff64b"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6787b643356111dfd4032b5bffe26d2f8331556ecb79e15dacb9275da02866e"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7c17b64b0a6ef4e5affae6a3724010a7a66bda48a62cfe0674dabd46642e8b54"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:27aa20d45c2e0b8cd05da6d4759649170e8dfc4f4e5ef33a34d06f2d79075d57"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d4f2cc7060dc3646632d7f15fe68e2fa98f58e35dd5666cd525f3b35d3fed7f8"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff46d772d5f6f73564979cd77a4fffe55c916a05f3cb70e7c9c0590059fb29ef"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:96323338e6c14e958d775700ec8a88346014a85e5de73ac7967db0367582049b"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:52421b41ac99e9d91934e4d0d0fe7da9f02bfa7536bb4431b4c05c906c8c6919"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7a7efd5b6d3e30d81ec68ab8a88252d7c7c6f13aaa875009fe3097eb4e30b84c"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ed777c1e8c99b63037b91f9d73a6aad20fd035d77ac84afcc205225f8f41188"}, - {file = "lxml-5.2.1-cp39-cp39-win32.whl", hash = "sha256:644df54d729ef810dcd0f7732e50e5ad1bd0a135278ed8d6bcb06f33b6b6f708"}, - {file = "lxml-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:9ca66b8e90daca431b7ca1408cae085d025326570e57749695d6a01454790e95"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b0ff53900566bc6325ecde9181d89afadc59c5ffa39bddf084aaedfe3b06a11"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd6037392f2d57793ab98d9e26798f44b8b4da2f2464388588f48ac52c489ea1"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9c07e7a45bb64e21df4b6aa623cb8ba214dfb47d2027d90eac197329bb5e94"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3249cc2989d9090eeac5467e50e9ec2d40704fea9ab72f36b034ea34ee65ca98"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f42038016852ae51b4088b2862126535cc4fc85802bfe30dea3500fdfaf1864e"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:533658f8fbf056b70e434dff7e7aa611bcacb33e01f75de7f821810e48d1bb66"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:622020d4521e22fb371e15f580d153134bfb68d6a429d1342a25f051ec72df1c"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efa7b51824aa0ee957ccd5a741c73e6851de55f40d807f08069eb4c5a26b2baa"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c6ad0fbf105f6bcc9300c00010a2ffa44ea6f555df1a2ad95c88f5656104817"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e233db59c8f76630c512ab4a4daf5a5986da5c3d5b44b8e9fc742f2a24dbd460"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a014510830df1475176466b6087fc0c08b47a36714823e58d8b8d7709132a96"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d38c8f50ecf57f0463399569aa388b232cf1a2ffb8f0a9a5412d0db57e054860"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5aea8212fb823e006b995c4dda533edcf98a893d941f173f6c9506126188860d"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff097ae562e637409b429a7ac958a20aab237a0378c42dabaa1e3abf2f896e5f"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f5d65c39f16717a47c36c756af0fb36144069c4718824b7533f803ecdf91138"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e32be23d538753a8adb6c85bd539f5fd3b15cb987404327c569dfc5fd8366e85"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cc518cea79fd1e2f6c90baafa28906d4309d24f3a63e801d855e7424c5b34144"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a0af35bd8ebf84888373630f73f24e86bf016642fb8576fba49d3d6b560b7cbc"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8aca2e3a72f37bfc7b14ba96d4056244001ddcc18382bd0daa087fd2e68a354"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ca1e8188b26a819387b29c3895c47a5e618708fe6f787f3b1a471de2c4a94d9"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c8ba129e6d3b0136a0f50345b2cb3db53f6bda5dd8c7f5d83fbccba97fb5dcb5"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e998e304036198b4f6914e6a1e2b6f925208a20e2042563d9734881150c6c246"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d3be9b2076112e51b323bdf6d5a7f8a798de55fb8d95fcb64bd179460cdc0704"}, - {file = "lxml-5.2.1.tar.gz", hash = "sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306"}, + {file = "lxml-5.2.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:364d03207f3e603922d0d3932ef363d55bbf48e3647395765f9bfcbdf6d23632"}, + {file = "lxml-5.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50127c186f191b8917ea2fb8b206fbebe87fd414a6084d15568c27d0a21d60db"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74e4f025ef3db1c6da4460dd27c118d8cd136d0391da4e387a15e48e5c975147"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981a06a3076997adf7c743dcd0d7a0415582661e2517c7d961493572e909aa1d"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aef5474d913d3b05e613906ba4090433c515e13ea49c837aca18bde190853dff"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e275ea572389e41e8b039ac076a46cb87ee6b8542df3fff26f5baab43713bca"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5b65529bb2f21ac7861a0e94fdbf5dc0daab41497d18223b46ee8515e5ad297"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bcc98f911f10278d1daf14b87d65325851a1d29153caaf146877ec37031d5f36"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:b47633251727c8fe279f34025844b3b3a3e40cd1b198356d003aa146258d13a2"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:fbc9d316552f9ef7bba39f4edfad4a734d3d6f93341232a9dddadec4f15d425f"}, + {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:13e69be35391ce72712184f69000cda04fc89689429179bc4c0ae5f0b7a8c21b"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b6a30a9ab040b3f545b697cb3adbf3696c05a3a68aad172e3fd7ca73ab3c835"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a233bb68625a85126ac9f1fc66d24337d6e8a0f9207b688eec2e7c880f012ec0"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:dfa7c241073d8f2b8e8dbc7803c434f57dbb83ae2a3d7892dd068d99e96efe2c"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a7aca7964ac4bb07680d5c9d63b9d7028cace3e2d43175cb50bba8c5ad33316"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ae4073a60ab98529ab8a72ebf429f2a8cc612619a8c04e08bed27450d52103c0"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ffb2be176fed4457e445fe540617f0252a72a8bc56208fd65a690fdb1f57660b"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e290d79a4107d7d794634ce3e985b9ae4f920380a813717adf61804904dc4393"}, + {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96e85aa09274955bb6bd483eaf5b12abadade01010478154b0ec70284c1b1526"}, + {file = "lxml-5.2.2-cp310-cp310-win32.whl", hash = "sha256:f956196ef61369f1685d14dad80611488d8dc1ef00be57c0c5a03064005b0f30"}, + {file = "lxml-5.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:875a3f90d7eb5c5d77e529080d95140eacb3c6d13ad5b616ee8095447b1d22e7"}, + {file = "lxml-5.2.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45f9494613160d0405682f9eee781c7e6d1bf45f819654eb249f8f46a2c22545"}, + {file = "lxml-5.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0b3f2df149efb242cee2ffdeb6674b7f30d23c9a7af26595099afaf46ef4e88"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d28cb356f119a437cc58a13f8135ab8a4c8ece18159eb9194b0d269ec4e28083"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:657a972f46bbefdbba2d4f14413c0d079f9ae243bd68193cb5061b9732fa54c1"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b9ea10063efb77a965a8d5f4182806fbf59ed068b3c3fd6f30d2ac7bee734"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07542787f86112d46d07d4f3c4e7c760282011b354d012dc4141cc12a68cef5f"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:303f540ad2dddd35b92415b74b900c749ec2010e703ab3bfd6660979d01fd4ed"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2eb2227ce1ff998faf0cd7fe85bbf086aa41dfc5af3b1d80867ecfe75fb68df3"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:1d8a701774dfc42a2f0b8ccdfe7dbc140500d1049e0632a611985d943fcf12df"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:56793b7a1a091a7c286b5f4aa1fe4ae5d1446fe742d00cdf2ffb1077865db10d"}, + {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb00b549b13bd6d884c863554566095bf6fa9c3cecb2e7b399c4bc7904cb33b5"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a2569a1f15ae6c8c64108a2cd2b4a858fc1e13d25846be0666fc144715e32ab"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:8cf85a6e40ff1f37fe0f25719aadf443686b1ac7652593dc53c7ef9b8492b115"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:d237ba6664b8e60fd90b8549a149a74fcc675272e0e95539a00522e4ca688b04"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0b3f5016e00ae7630a4b83d0868fca1e3d494c78a75b1c7252606a3a1c5fc2ad"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23441e2b5339bc54dc949e9e675fa35efe858108404ef9aa92f0456929ef6fe8"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb0ba3e8566548d6c8e7dd82a8229ff47bd8fb8c2da237607ac8e5a1b8312e5"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:79d1fb9252e7e2cfe4de6e9a6610c7cbb99b9708e2c3e29057f487de5a9eaefa"}, + {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6dcc3d17eac1df7859ae01202e9bb11ffa8c98949dcbeb1069c8b9a75917e01b"}, + {file = "lxml-5.2.2-cp311-cp311-win32.whl", hash = "sha256:4c30a2f83677876465f44c018830f608fa3c6a8a466eb223535035fbc16f3438"}, + {file = "lxml-5.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:49095a38eb333aaf44c06052fd2ec3b8f23e19747ca7ec6f6c954ffea6dbf7be"}, + {file = "lxml-5.2.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7429e7faa1a60cad26ae4227f4dd0459efde239e494c7312624ce228e04f6391"}, + {file = "lxml-5.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:50ccb5d355961c0f12f6cf24b7187dbabd5433f29e15147a67995474f27d1776"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc911208b18842a3a57266d8e51fc3cfaccee90a5351b92079beed912a7914c2"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33ce9e786753743159799fdf8e92a5da351158c4bfb6f2db0bf31e7892a1feb5"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec87c44f619380878bd49ca109669c9f221d9ae6883a5bcb3616785fa8f94c97"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08ea0f606808354eb8f2dfaac095963cb25d9d28e27edcc375d7b30ab01abbf6"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75a9632f1d4f698b2e6e2e1ada40e71f369b15d69baddb8968dcc8e683839b18"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74da9f97daec6928567b48c90ea2c82a106b2d500f397eeb8941e47d30b1ca85"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:0969e92af09c5687d769731e3f39ed62427cc72176cebb54b7a9d52cc4fa3b73"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:9164361769b6ca7769079f4d426a41df6164879f7f3568be9086e15baca61466"}, + {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d26a618ae1766279f2660aca0081b2220aca6bd1aa06b2cf73f07383faf48927"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab67ed772c584b7ef2379797bf14b82df9aa5f7438c5b9a09624dd834c1c1aaf"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3d1e35572a56941b32c239774d7e9ad724074d37f90c7a7d499ab98761bd80cf"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:8268cbcd48c5375f46e000adb1390572c98879eb4f77910c6053d25cc3ac2c67"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e282aedd63c639c07c3857097fc0e236f984ceb4089a8b284da1c526491e3f3d"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfdc2bfe69e9adf0df4915949c22a25b39d175d599bf98e7ddf620a13678585"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4aefd911793b5d2d7a921233a54c90329bf3d4a6817dc465f12ffdfe4fc7b8fe"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8b8df03a9e995b6211dafa63b32f9d405881518ff1ddd775db4e7b98fb545e1c"}, + {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f11ae142f3a322d44513de1018b50f474f8f736bc3cd91d969f464b5bfef8836"}, + {file = "lxml-5.2.2-cp312-cp312-win32.whl", hash = "sha256:16a8326e51fcdffc886294c1e70b11ddccec836516a343f9ed0f82aac043c24a"}, + {file = "lxml-5.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:bbc4b80af581e18568ff07f6395c02114d05f4865c2812a1f02f2eaecf0bfd48"}, + {file = "lxml-5.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e3d9d13603410b72787579769469af730c38f2f25505573a5888a94b62b920f8"}, + {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38b67afb0a06b8575948641c1d6d68e41b83a3abeae2ca9eed2ac59892b36706"}, + {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c689d0d5381f56de7bd6966a4541bff6e08bf8d3871bbd89a0c6ab18aa699573"}, + {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:cf2a978c795b54c539f47964ec05e35c05bd045db5ca1e8366988c7f2fe6b3ce"}, + {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:739e36ef7412b2bd940f75b278749106e6d025e40027c0b94a17ef7968d55d56"}, + {file = "lxml-5.2.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d8bbcd21769594dbba9c37d3c819e2d5847656ca99c747ddb31ac1701d0c0ed9"}, + {file = "lxml-5.2.2-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:2304d3c93f2258ccf2cf7a6ba8c761d76ef84948d87bf9664e14d203da2cd264"}, + {file = "lxml-5.2.2-cp36-cp36m-win32.whl", hash = "sha256:02437fb7308386867c8b7b0e5bc4cd4b04548b1c5d089ffb8e7b31009b961dc3"}, + {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, + {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, + {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, + {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, + {file = "lxml-5.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7ed07b3062b055d7a7f9d6557a251cc655eed0b3152b76de619516621c56f5d3"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f60fdd125d85bf9c279ffb8e94c78c51b3b6a37711464e1f5f31078b45002421"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a7e24cb69ee5f32e003f50e016d5fde438010c1022c96738b04fc2423e61706"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23cfafd56887eaed93d07bc4547abd5e09d837a002b791e9767765492a75883f"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:19b4e485cd07b7d83e3fe3b72132e7df70bfac22b14fe4bf7a23822c3a35bff5"}, + {file = "lxml-5.2.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7ce7ad8abebe737ad6143d9d3bf94b88b93365ea30a5b81f6877ec9c0dee0a48"}, + {file = "lxml-5.2.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e49b052b768bb74f58c7dda4e0bdf7b79d43a9204ca584ffe1fb48a6f3c84c66"}, + {file = "lxml-5.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d14a0d029a4e176795cef99c056d58067c06195e0c7e2dbb293bf95c08f772a3"}, + {file = "lxml-5.2.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:be49ad33819d7dcc28a309b86d4ed98e1a65f3075c6acd3cd4fe32103235222b"}, + {file = "lxml-5.2.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a6d17e0370d2516d5bb9062c7b4cb731cff921fc875644c3d751ad857ba9c5b1"}, + {file = "lxml-5.2.2-cp38-cp38-win32.whl", hash = "sha256:5b8c041b6265e08eac8a724b74b655404070b636a8dd6d7a13c3adc07882ef30"}, + {file = "lxml-5.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:f61efaf4bed1cc0860e567d2ecb2363974d414f7f1f124b1df368bbf183453a6"}, + {file = "lxml-5.2.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb91819461b1b56d06fa4bcf86617fac795f6a99d12239fb0c68dbeba41a0a30"}, + {file = "lxml-5.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d4ed0c7cbecde7194cd3228c044e86bf73e30a23505af852857c09c24e77ec5d"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54401c77a63cc7d6dc4b4e173bb484f28a5607f3df71484709fe037c92d4f0ed"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:625e3ef310e7fa3a761d48ca7ea1f9d8718a32b1542e727d584d82f4453d5eeb"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:519895c99c815a1a24a926d5b60627ce5ea48e9f639a5cd328bda0515ea0f10c"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7079d5eb1c1315a858bbf180000757db8ad904a89476653232db835c3114001"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:343ab62e9ca78094f2306aefed67dcfad61c4683f87eee48ff2fd74902447726"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:cd9e78285da6c9ba2d5c769628f43ef66d96ac3085e59b10ad4f3707980710d3"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:546cf886f6242dff9ec206331209db9c8e1643ae642dea5fdbecae2453cb50fd"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:02f6a8eb6512fdc2fd4ca10a49c341c4e109aa6e9448cc4859af5b949622715a"}, + {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:339ee4a4704bc724757cd5dd9dc8cf4d00980f5d3e6e06d5847c1b594ace68ab"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0a028b61a2e357ace98b1615fc03f76eb517cc028993964fe08ad514b1e8892d"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f90e552ecbad426eab352e7b2933091f2be77115bb16f09f78404861c8322981"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d83e2d94b69bf31ead2fa45f0acdef0757fa0458a129734f59f67f3d2eb7ef32"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a02d3c48f9bb1e10c7788d92c0c7db6f2002d024ab6e74d6f45ae33e3d0288a3"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d68ce8e7b2075390e8ac1e1d3a99e8b6372c694bbe612632606d1d546794207"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:453d037e09a5176d92ec0fd282e934ed26d806331a8b70ab431a81e2fbabf56d"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3b019d4ee84b683342af793b56bb35034bd749e4cbdd3d33f7d1107790f8c472"}, + {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb3942960f0beb9f46e2a71a3aca220d1ca32feb5a398656be934320804c0df9"}, + {file = "lxml-5.2.2-cp39-cp39-win32.whl", hash = "sha256:ac6540c9fff6e3813d29d0403ee7a81897f1d8ecc09a8ff84d2eea70ede1cdbf"}, + {file = "lxml-5.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:610b5c77428a50269f38a534057444c249976433f40f53e3b47e68349cca1425"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b537bd04d7ccd7c6350cdaaaad911f6312cbd61e6e6045542f781c7f8b2e99d2"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4820c02195d6dfb7b8508ff276752f6b2ff8b64ae5d13ebe02e7667e035000b9"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a09f6184f17a80897172863a655467da2b11151ec98ba8d7af89f17bf63dae"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:76acba4c66c47d27c8365e7c10b3d8016a7da83d3191d053a58382311a8bf4e1"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b128092c927eaf485928cec0c28f6b8bead277e28acf56800e972aa2c2abd7a2"}, + {file = "lxml-5.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ae791f6bd43305aade8c0e22f816b34f3b72b6c820477aab4d18473a37e8090b"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a2f6a1bc2460e643785a2cde17293bd7a8f990884b822f7bca47bee0a82fc66b"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e8d351ff44c1638cb6e980623d517abd9f580d2e53bfcd18d8941c052a5a009"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bec4bd9133420c5c52d562469c754f27c5c9e36ee06abc169612c959bd7dbb07"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:55ce6b6d803890bd3cc89975fca9de1dff39729b43b73cb15ddd933b8bc20484"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8ab6a358d1286498d80fe67bd3d69fcbc7d1359b45b41e74c4a26964ca99c3f8"}, + {file = "lxml-5.2.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:06668e39e1f3c065349c51ac27ae430719d7806c026fec462e5693b08b95696b"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9cd5323344d8ebb9fb5e96da5de5ad4ebab993bbf51674259dbe9d7a18049525"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89feb82ca055af0fe797a2323ec9043b26bc371365847dbe83c7fd2e2f181c34"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e481bba1e11ba585fb06db666bfc23dbe181dbafc7b25776156120bf12e0d5a6"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d6c6ea6a11ca0ff9cd0390b885984ed31157c168565702959c25e2191674a14"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3d98de734abee23e61f6b8c2e08a88453ada7d6486dc7cdc82922a03968928db"}, + {file = "lxml-5.2.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:69ab77a1373f1e7563e0fb5a29a8440367dec051da6c7405333699d07444f511"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:34e17913c431f5ae01d8658dbf792fdc457073dcdfbb31dc0cc6ab256e664a8d"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f8757b03208c3f50097761be2dea0aba02e94f0dc7023ed73a7bb14ff11eb0"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a520b4f9974b0a0a6ed73c2154de57cdfd0c8800f4f15ab2b73238ffed0b36e"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5e097646944b66207023bc3c634827de858aebc226d5d4d6d16f0b77566ea182"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b5e4ef22ff25bfd4ede5f8fb30f7b24446345f3e79d9b7455aef2836437bc38a"}, + {file = "lxml-5.2.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff69a9a0b4b17d78170c73abe2ab12084bdf1691550c5629ad1fe7849433f324"}, + {file = "lxml-5.2.2.tar.gz", hash = "sha256:bb2dc4898180bea79863d5487e5f9c7c34297414bad54bcd0f0852aee9cfdb87"}, ] [package.extras] @@ -865,13 +849,13 @@ source = ["Cython (>=3.0.10)"] [[package]] name = "mako" -version = "1.3.3" +version = "1.3.4" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" files = [ - {file = "Mako-1.3.3-py3-none-any.whl", hash = "sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40"}, - {file = "Mako-1.3.3.tar.gz", hash = "sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73"}, + {file = "Mako-1.3.4-py3-none-any.whl", hash = "sha256:a080ace85c3ceb6226d85e124608607b9ee072a87ae3ea22561435e8e6738125"}, + {file = "Mako-1.3.4.tar.gz", hash = "sha256:029a10f5fc497f5be7f7754e4ef5bcf26cb93044798de31fd1303f777f360916"}, ] [package.dependencies] @@ -1062,13 +1046,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.7.0" +version = "3.7.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, - {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, + {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, + {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, ] [package.dependencies] @@ -1901,13 +1885,13 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.26.1" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"}, - {file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] From 437368e5c13b73dfe6f891b47fb46b17afbbbcb4 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 11:16:38 +0000 Subject: [PATCH 49/55] Fix trivy image scanning --- .github/workflows/ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da01aaa0..b4441d47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: run: poetry install - name: Generate SBOM - run: poetry run cyclonedx-py poetry > sbom.json + run: poetry run cyclonedx-py poetry --without test,dev > sbom.json - name: Generate licenses file run: | @@ -168,6 +168,13 @@ jobs: env: DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index + - name: print metadata + run: | + echo "tags: ${{ steps.meta.outputs.tags }}" + echo "labels: ${{ steps.meta.outputs.labels }}" + echo "annotations: ${{ steps.meta.outputs.annotations }}" + + - name: Build and push Docker image uses: docker/build-push-action@v5 with: @@ -182,7 +189,7 @@ jobs: - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: - image-ref: ${{ steps.meta.outputs.tags[0] }} + image-ref: ${{ steps.meta.outputs.tags }} trivy-config: trivy.yaml scan-type: image exit-code: 0 From 82b2b1458a2545250ba18170c430f5ae492913ee Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 12:45:41 +0000 Subject: [PATCH 50/55] Fix build step in documentation --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 17713073..2dc92883 100644 --- a/BUILD.md +++ b/BUILD.md @@ -20,7 +20,7 @@ poetry install When poetry is done installing all dependencies you can start using the tool. ```shell -poetry run python -m uvicorn tad.main:app +poetry run python -m granian --interface asgi tad.main:app ``` ## Database From 47efa5e7f7d9dce645fa77b556287524a4f03246 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 12:49:47 +0000 Subject: [PATCH 51/55] Fix typo in state-pr-schedule --- .github/workflows/stale-pr-schedule.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale-pr-schedule.yml b/.github/workflows/stale-pr-schedule.yml index a1c2395f..a695d5bb 100644 --- a/.github/workflows/stale-pr-schedule.yml +++ b/.github/workflows/stale-pr-schedule.yml @@ -1,4 +1,4 @@ -name: stale-shedule +name: stale-schedule on: schedule: - cron: "0 4 * * *" From 4d32b551c1d68278a7778c03a1ad86a2ca75bb98 Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 13:07:47 +0000 Subject: [PATCH 52/55] Fix fatal role error in compose postgres setup --- compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose.yml b/compose.yml index 324d3887..17029737 100644 --- a/compose.yml +++ b/compose.yml @@ -30,8 +30,8 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Variable not set} - SECRET_KEY=${SECRET_KEY:?Variable not set} healthcheck: - test: ["CMD-SHELL", "pg_isready", "-d", "tad", "-U", "postgres"] - start_period: 10s + test: ["CMD-SHELL", "pg_isready", "-d", "tad"] + start_period: 3s db-admin: From b14b676d3b2464e952c97c7d54233d7aafb68c2f Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 13:30:58 +0000 Subject: [PATCH 53/55] Add healthchecks --- compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compose.yml b/compose.yml index 17029737..7fb6e1fe 100644 --- a/compose.yml +++ b/compose.yml @@ -15,6 +15,8 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Variable not set} ports: - 8000:8000 + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8000/health/live || exit 1"] db: image: postgres:16 @@ -46,6 +48,8 @@ services: depends_on: db: condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-O", "-", "http://localhost:8080/misc/ping"] #TODO(berry): Traefik From bf943a988360b74bb63d85533c25bf758aa9a82c Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 13:40:59 +0000 Subject: [PATCH 54/55] Fix database healthcheck from cmd-exec to cmd --- compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose.yml b/compose.yml index 7fb6e1fe..4622e6fb 100644 --- a/compose.yml +++ b/compose.yml @@ -32,7 +32,7 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Variable not set} - SECRET_KEY=${SECRET_KEY:?Variable not set} healthcheck: - test: ["CMD-SHELL", "pg_isready", "-d", "tad"] + test: ["CMD", "pg_isready", "-q", "-d", "tad", "-U", "tad"] start_period: 3s From ad1e39a8d56e4f861060ce2edf4be7acc8f78dfb Mon Sep 17 00:00:00 2001 From: Berry den Hartog <38954346+berrydenhartog@users.noreply.github.com> Date: Tue, 14 May 2024 14:39:45 +0000 Subject: [PATCH 55/55] Remove downloads from readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index eeb585e3..21c62a1f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/minbzk/tad/ci.yml?label=tests) ![Sonar Coverage](https://img.shields.io/sonar/coverage/ai-validation-team_tad?server=https%3A%2F%2Fsonarcloud.io&label=coverage(sonar)) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ai-validation-team_tad&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ai-validation-team_tad) -![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/minbzk/tad/total) ![GitHub Release](https://img.shields.io/github/v/release/minbzk/tad?include_prereleases&sort=semver) ![GitHub License](https://img.shields.io/github/license/minbzk/tad)