From 1a9c45981038455af172343e2ecd421c3940cbd6 Mon Sep 17 00:00:00 2001 From: rnetser Date: Tue, 25 Jun 2024 14:52:42 +0300 Subject: [PATCH 1/7] Move to using FastAPI --- README.md | 21 +- docker-compose-example.yaml | 2 +- entrypoint.sh | 7 +- poetry.lock | 831 +++++++++++++++++- pyproject.toml | 4 +- renovate.json | 2 +- webhook_server_container/app.py | 40 +- webhook_server_container/libs/github_api.py | 82 +- webhook_server_container/libs/jira_api.py | 10 +- webhook_server_container/libs/label.py | 8 + webhook_server_container/utils/constants.py | 9 +- .../utils/github_repository_settings.py | 61 +- webhook_server_container/utils/helpers.py | 22 +- webhook_server_container/utils/webhook.py | 21 +- 14 files changed, 923 insertions(+), 197 deletions(-) create mode 100644 webhook_server_container/libs/label.py diff --git a/README.md b/README.md index d3be6dd0..f6f0c993 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # github-webhook-server -A Flask-based webhook server for managing GitHub repositories. It handles tasks such as repository setup, branch protection, and webhook configuration. +A [FastAPI-based](https://fastapi.tiangolo.com) webhook server for managing GitHub repositories. It handles tasks such as repository setup, branch protection, and webhook configuration. Pre-build container images available in: @@ -264,22 +264,3 @@ Webhooks are automatically created for GitHub repositories based on settings def ### Usage Guide To use the webhook server, first prepare the `config.yaml` file with the necessary repository and server configurations. Set the required environment variables, including `WEBHOOK_SERVER_LOG_FILE` and `WEBHOOK_SERVER_DATA_DIR`. Build and start the server using the instructions in the 'Build container' section. - -### Development - -To run locally you need to export some os environment variables - -```bash -poetry install - -WEBHOOK_SERVER_DATA_DIR=/tmp/webhook_server_data - -mkdir -p $WEBHOOK_SERVER_DATA_DIR -cp -f webhook-server.private-key.pem $WEBHOOK_SERVER_DATA_DIR/webhook-server.private-key.pem -cp -f config.yaml $WEBHOOK_SERVER_DATA_DIR/config.yaml -export WEBHOOK_SERVER_PORT=5003 - -export FLASK_DEBUG=1 -export WEBHOOK_SERVER_DATA_DIR=$WEBHOOK_SERVER_DATA_DIR -poetry run python webhook_server_container/app.py -``` diff --git a/docker-compose-example.yaml b/docker-compose-example.yaml index 089fc84c..9448a8a1 100644 --- a/docker-compose-example.yaml +++ b/docker-compose-example.yaml @@ -6,11 +6,11 @@ services: - ./containers:/containers:Z # optional, to cache podman pull containers environment: - WEBHOOK_SERVER_LOG_FILE=/tmp/webhook_server.log - - FLASK_DEBUG=1 # Debug Flask server to get logs to console. - PUID=1000 - PGID=1000 - TZ=Asia/Jerusalem - DEVELOPMENT=false # Set to true when developing. + - UVICORN_MAX_WORKERS=50 # defaults to 10 if not set and running in production ports: - "5000:5000" privileged: true diff --git a/entrypoint.sh b/entrypoint.sh index ceb269d9..23ebbdea 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,12 +1,15 @@ #!/bin/bash +SERVER_RUN_CMD="poetry run uvicorn webhook_server_container.app:FastAPI_APP " +UVICORN_WORKERS="${UVICORN_MAX_WORKERS:=10}" + set -ep poetry run python webhook_server_container/utils/github_repository_settings.py poetry run python webhook_server_container/utils/webhook.py if [[ -z $DEVELOPMENT ]]; then - poetry run uwsgi --disable-logging --post-buffering --master --enable-threads --http 0.0.0.0:5000 --wsgi-file webhook_server_container/app.py --callable FLASK_APP --processes 4 --threads 2 + eval "${SERVER_RUN_CMD} --workers ${UVICORN_WORKERS}" else - poetry run python webhook_server_container/app.py + eval "${SERVER_RUN_CMD} --reload" fi diff --git a/poetry.lock b/poetry.lock index e4b851ad..536243fd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,40 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "anyio" +version = "4.4.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, +] + +[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 = "appnope" @@ -80,17 +116,6 @@ files = [ tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] -[[package]] -name = "blinker" -version = "1.8.2" -description = "Fast, simple object-to-object and broadcast signaling" -optional = false -python-versions = ">=3.8" -files = [ - {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, - {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, -] - [[package]] name = "build" version = "1.2.1" @@ -483,6 +508,26 @@ 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 = "dulwich" version = "0.21.7" @@ -584,6 +629,35 @@ files = [ [package.dependencies] packaging = ">=20.9" +[[package]] +name = "email-validator" +version = "2.2.0" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, + {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "executing" version = "2.0.1" @@ -598,6 +672,50 @@ files = [ [package.extras] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] +[[package]] +name = "fastapi" +version = "0.111.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.111.0-py3-none-any.whl", hash = "sha256:97ecbf994be0bcbdadedf88c3150252bed7b2087075ac99735403b1b76cc8fc0"}, + {file = "fastapi-0.111.0.tar.gz", hash = "sha256:b9db9dd147c91cb8b769f7183535773d8741dd46f9dc6676cd82eab510228cd7"}, +] + +[package.dependencies] +email_validator = ">=2.0.0" +fastapi-cli = ">=0.0.2" +httpx = ">=0.23.0" +jinja2 = ">=2.11.2" +orjson = ">=3.2.1" +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" +python-multipart = ">=0.0.7" +starlette = ">=0.37.2,<0.38.0" +typing-extensions = ">=4.8.0" +ujson = ">=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" +uvicorn = {version = ">=0.12.0", extras = ["standard"]} + +[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 = "fastapi-cli" +version = "0.0.4" +description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"}, + {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, +] + +[package.dependencies] +typer = ">=0.12.3" + +[package.extras] +standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] + [[package]] name = "fastjsonschema" version = "2.19.1" @@ -629,27 +747,108 @@ testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", typing = ["typing-extensions (>=4.8)"] [[package]] -name = "flask" -version = "3.0.3" -description = "A simple framework for building complex web applications." +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 = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, - {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, ] [package.dependencies] -blinker = ">=1.6.2" -click = ">=8.1.3" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} -itsdangerous = ">=2.1.2" -Jinja2 = ">=3.1.2" -Werkzeug = ">=3.0.0" +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" [package.extras] -async = ["asgiref (>=3.2)"] -dotenv = ["python-dotenv"] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] [[package]] name = "idna" @@ -765,17 +964,6 @@ qtconsole = ["qtconsole"] test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] -[[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 = "jaraco-classes" version = "3.4.0" @@ -1120,6 +1308,61 @@ rsa = ["cryptography (>=3.0.0)"] signals = ["blinker (>=1.4.0)"] signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] +[[package]] +name = "orjson" +version = "3.10.5" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:545d493c1f560d5ccfc134803ceb8955a14c3fcb47bbb4b2fee0232646d0b932"}, + {file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4324929c2dd917598212bfd554757feca3e5e0fa60da08be11b4aa8b90013c1"}, + {file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c13ca5e2ddded0ce6a927ea5a9f27cae77eee4c75547b4297252cb20c4d30e6"}, + {file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6c8e30adfa52c025f042a87f450a6b9ea29649d828e0fec4858ed5e6caecf63"}, + {file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:338fd4f071b242f26e9ca802f443edc588fa4ab60bfa81f38beaedf42eda226c"}, + {file = "orjson-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6970ed7a3126cfed873c5d21ece1cd5d6f83ca6c9afb71bbae21a0b034588d96"}, + {file = "orjson-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:235dadefb793ad12f7fa11e98a480db1f7c6469ff9e3da5e73c7809c700d746b"}, + {file = "orjson-3.10.5-cp310-none-win32.whl", hash = "sha256:be79e2393679eda6a590638abda16d167754393f5d0850dcbca2d0c3735cebe2"}, + {file = "orjson-3.10.5-cp310-none-win_amd64.whl", hash = "sha256:c4a65310ccb5c9910c47b078ba78e2787cb3878cdded1702ac3d0da71ddc5228"}, + {file = "orjson-3.10.5-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cdf7365063e80899ae3a697def1277c17a7df7ccfc979990a403dfe77bb54d40"}, + {file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b68742c469745d0e6ca5724506858f75e2f1e5b59a4315861f9e2b1df77775a"}, + {file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7d10cc1b594951522e35a3463da19e899abe6ca95f3c84c69e9e901e0bd93d38"}, + {file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcbe82b35d1ac43b0d84072408330fd3295c2896973112d495e7234f7e3da2e1"}, + {file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c0eb7e0c75e1e486c7563fe231b40fdd658a035ae125c6ba651ca3b07936f5"}, + {file = "orjson-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:53ed1c879b10de56f35daf06dbc4a0d9a5db98f6ee853c2dbd3ee9d13e6f302f"}, + {file = "orjson-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:099e81a5975237fda3100f918839af95f42f981447ba8f47adb7b6a3cdb078fa"}, + {file = "orjson-3.10.5-cp311-none-win32.whl", hash = "sha256:1146bf85ea37ac421594107195db8bc77104f74bc83e8ee21a2e58596bfb2f04"}, + {file = "orjson-3.10.5-cp311-none-win_amd64.whl", hash = "sha256:36a10f43c5f3a55c2f680efe07aa93ef4a342d2960dd2b1b7ea2dd764fe4a37c"}, + {file = "orjson-3.10.5-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:68f85ecae7af14a585a563ac741b0547a3f291de81cd1e20903e79f25170458f"}, + {file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28afa96f496474ce60d3340fe8d9a263aa93ea01201cd2bad844c45cd21f5268"}, + {file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cd684927af3e11b6e754df80b9ffafd9fb6adcaa9d3e8fdd5891be5a5cad51e"}, + {file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d21b9983da032505f7050795e98b5d9eee0df903258951566ecc358f6696969"}, + {file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ad1de7fef79736dde8c3554e75361ec351158a906d747bd901a52a5c9c8d24b"}, + {file = "orjson-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d97531cdfe9bdd76d492e69800afd97e5930cb0da6a825646667b2c6c6c0211"}, + {file = "orjson-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d69858c32f09c3e1ce44b617b3ebba1aba030e777000ebdf72b0d8e365d0b2b3"}, + {file = "orjson-3.10.5-cp312-none-win32.whl", hash = "sha256:64c9cc089f127e5875901ac05e5c25aa13cfa5dbbbd9602bda51e5c611d6e3e2"}, + {file = "orjson-3.10.5-cp312-none-win_amd64.whl", hash = "sha256:b2efbd67feff8c1f7728937c0d7f6ca8c25ec81373dc8db4ef394c1d93d13dc5"}, + {file = "orjson-3.10.5-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:03b565c3b93f5d6e001db48b747d31ea3819b89abf041ee10ac6988886d18e01"}, + {file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:584c902ec19ab7928fd5add1783c909094cc53f31ac7acfada817b0847975f26"}, + {file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a35455cc0b0b3a1eaf67224035f5388591ec72b9b6136d66b49a553ce9eb1e6"}, + {file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1670fe88b116c2745a3a30b0f099b699a02bb3482c2591514baf5433819e4f4d"}, + {file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185c394ef45b18b9a7d8e8f333606e2e8194a50c6e3c664215aae8cf42c5385e"}, + {file = "orjson-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ca0b3a94ac8d3886c9581b9f9de3ce858263865fdaa383fbc31c310b9eac07c9"}, + {file = "orjson-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dfc91d4720d48e2a709e9c368d5125b4b5899dced34b5400c3837dadc7d6271b"}, + {file = "orjson-3.10.5-cp38-none-win32.whl", hash = "sha256:c05f16701ab2a4ca146d0bca950af254cb7c02f3c01fca8efbbad82d23b3d9d4"}, + {file = "orjson-3.10.5-cp38-none-win_amd64.whl", hash = "sha256:8a11d459338f96a9aa7f232ba95679fc0c7cedbd1b990d736467894210205c09"}, + {file = "orjson-3.10.5-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:85c89131d7b3218db1b24c4abecea92fd6c7f9fab87441cfc342d3acc725d807"}, + {file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66215277a230c456f9038d5e2d84778141643207f85336ef8d2a9da26bd7ca"}, + {file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51bbcdea96cdefa4a9b4461e690c75ad4e33796530d182bdd5c38980202c134a"}, + {file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbead71dbe65f959b7bd8cf91e0e11d5338033eba34c114f69078d59827ee139"}, + {file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df58d206e78c40da118a8c14fc189207fffdcb1f21b3b4c9c0c18e839b5a214"}, + {file = "orjson-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c4057c3b511bb8aef605616bd3f1f002a697c7e4da6adf095ca5b84c0fd43595"}, + {file = "orjson-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b39e006b00c57125ab974362e740c14a0c6a66ff695bff44615dcf4a70ce2b86"}, + {file = "orjson-3.10.5-cp39-none-win32.whl", hash = "sha256:eded5138cc565a9d618e111c6d5c2547bbdd951114eb822f7f6309e04db0fb47"}, + {file = "orjson-3.10.5-cp39-none-win_amd64.whl", hash = "sha256:cc28e90a7cae7fcba2493953cff61da5a52950e78dc2dacfe931a317ee3d8de7"}, + {file = "orjson-3.10.5.tar.gz", hash = "sha256:7a5baef8a4284405d96c90c7c62b755e9ef1ada84c2406c24a9ebec86b89f46d"}, +] + [[package]] name = "packaging" version = "24.1" @@ -1452,6 +1695,116 @@ files = [ {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] +[[package]] +name = "pydantic" +version = "2.7.4" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, + {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.4" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, + {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, + {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, + {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, + {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, + {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, + {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, + {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, + {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, + {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, + {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, + {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, + {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, + {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + [[package]] name = "pygithub" version = "2.3.0" @@ -1559,6 +1912,34 @@ files = [ {file = "pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965"}, ] +[[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 = "python-rrmngmnt" version = "0.1.32" @@ -1910,6 +2291,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 = "stack-data" version = "0.6.3" @@ -1929,6 +2321,24 @@ pure-eval = "*" [package.extras] tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] +[[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" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + [[package]] name = "timeout-sampler" version = "0.0.34" @@ -1990,6 +2400,23 @@ files = [ {file = "trove_classifiers-2024.5.22.tar.gz", hash = "sha256:8a6242bbb5c9ae88d34cf665e816b287d2212973c8777dfaef5ec18d72ac1d03"}, ] +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + [[package]] name = "typing-extensions" version = "4.12.2" @@ -2001,6 +2428,93 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +[[package]] +name = "ujson" +version = "5.10.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, + {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, + {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, + {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, + {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, + {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, + {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, + {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, + {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, + {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, + {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, + {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, + {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, + {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, +] + [[package]] name = "urllib3" version = "2.2.1" @@ -2018,6 +2532,76 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "uvicorn" +version = "0.30.1" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, + {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, +] + +[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 = "uwsgi" version = "2.0.26" @@ -2048,6 +2632,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.22.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, + {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, + {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, + {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, + {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, + {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, + {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, + {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, + {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, + {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, + {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, + {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, + {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, + {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + [[package]] name = "wcwidth" version = "0.2.13" @@ -2060,22 +2731,86 @@ files = [ ] [[package]] -name = "werkzeug" -version = "3.0.3" -description = "The comprehensive WSGI web application library." +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.8" files = [ - {file = "werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8"}, - {file = "werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18"}, + {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"}, ] -[package.dependencies] -MarkupSafe = ">=2.1.1" - -[package.extras] -watchdog = ["watchdog (>=2.3)"] - [[package]] name = "wrapt" version = "1.16.0" @@ -2246,4 +2981,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "fa4e56b18d0f9a5b53a912a8758a2d927c8d39433cb83e04cf37bf7194f2816d" +content-hash = "3bef3f259028c362632c2fa8387c215fec34e040c0f7d06749feb6eb63955fb9" diff --git a/pyproject.toml b/pyproject.toml index 8acba6fb..f302cdec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,6 @@ Download = "https://quay.io/repository/myakove/github-webhook-server" python = "^3.8" poetry-dynamic-versioning = {extras = ["plugin"], version = "^1.0.0"} pygithub = "^2.0.0" -flask = "^3.0.0" pyyaml = "^6.0" build = "^1.0.0" shortuuid = "^1.0.11" @@ -43,6 +42,9 @@ requests = "^2.31.0" jira = "^3.8.0" pyhelper-utils = "^0.0.25" uwsgi = "^2.0.26" +fastapi = "^0.111.0" +python-simple-logger = "^1.0.30" +uvicorn = "^0.30.1" [tool.poetry.group.dev.dependencies] ipdb = "^0.13.13" diff --git a/renovate.json b/renovate.json index 41d54155..f5d5eed2 100644 --- a/renovate.json +++ b/renovate.json @@ -1 +1 @@ -{"$schema": "https://docs.renovatebot.com/renovate-schema.json", "packageRules": [{"matchPackagePatterns": ["poetry-dynamic-versioning", "pygithub", "flask", "pyyaml", "build", "shortuuid", "colorlog", "colorama", "ruff", "timeout-sampler", "requests", "jira", "pyhelper-utils", "uwsgi", "ipdb", "ipython"], "groupName": "poetry-deps"}]} +{"$schema": "https://docs.renovatebot.com/renovate-schema.json", "packageRules": [{"matchPackagePatterns": ["poetry-dynamic-versioning", "pygithub", "pyyaml", "build", "shortuuid", "colorlog", "colorama", "ruff", "timeout-sampler", "requests", "jira", "pyhelper-utils", "uwsgi", "fastapi", "python-simple-logger", "uvicorn", "ipdb", "ipython"], "groupName": "poetry-deps"}]} diff --git a/webhook_server_container/app.py b/webhook_server_container/app.py index c8a6a114..8a8d0764 100644 --- a/webhook_server_container/app.py +++ b/webhook_server_container/app.py @@ -1,59 +1,47 @@ -import os - +import requests import urllib3 from flask import request +from simple_logger.logger import get_logger from webhook_server_container.libs.github_api import GitHubApi from webhook_server_container.utils.constants import ( APP_ROOT_PATH, - FLASK_APP, + FastAPI_APP, ) REPOSITORIES_APP_API = {} MISSING_APP_REPOSITORIES = [] urllib3.disable_warnings() +LOGGER = get_logger(name="app") + -@FLASK_APP.route(f"{APP_ROOT_PATH}/healthcheck") +@FastAPI_APP.get(f"{APP_ROOT_PATH}/healthcheck") def healthcheck(): - return "alive" + return {"status": requests.status_codes.codes.ok, "message": "Alive"} -@FLASK_APP.route(APP_ROOT_PATH, methods=["POST"]) -def process_webhook(): - process_failed_msg = "Process failed" +@FastAPI_APP.post(APP_ROOT_PATH) +async def process_webhook(): + process_failed_msg = {"status": requests.status_codes.codes.server_error, "Message": "Process failed"} try: hook_data = request.json except Exception as ex: - FLASK_APP.logger.error(f"Error get JSON from request: {ex}") + LOGGER.error(f"Error get JSON from request: {ex}") return process_failed_msg try: api = GitHubApi(hook_data=hook_data) except Exception as ex: - FLASK_APP.logger.error(f"Failed to initialized GitHubApi instance: {ex}") + LOGGER.error(f"Failed to initialized GitHubApi instance: {ex}") return process_failed_msg github_event = request.headers.get("X-GitHub-Event") event_log = f"Event type: {github_event}. event ID: {request.headers.get('X-GitHub-Delivery')}" try: api.process_hook(data=github_event, event_log=event_log) - return "process success" + return {"status": requests.status_codes.codes.ok, "Message": "process success"} except Exception as ex: - FLASK_APP.logger.error(f"Failed to process hook: {ex}") + LOGGER.error(f"Failed to process hook: {ex}") return process_failed_msg - - -def main(): - FLASK_APP.logger.info(f"Starting {FLASK_APP.name} app") - FLASK_APP.run( - port=5000, - host="0.0.0.0", - use_reloader=bool(os.getenv("WEBHOOK_SERVER_USE_RELOAD", False)), - debug=bool(os.getenv("WEBHOOK_SERVER_USE_DEBUG", False)), - ) - - -if __name__ == "__main__": - main() diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index cde1caf3..c03ef6c7 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -12,6 +12,7 @@ import yaml from github import GithubException from github.GithubException import UnknownObjectException +from simple_logger.logger import get_logger from timeout_sampler import TimeoutSampler, TimeoutExpiredError from webhook_server_container.libs.config import Config @@ -30,7 +31,7 @@ DELETE_STR, DYNAMIC_LABELS_DICT, FAILURE_STR, - FLASK_APP, + FastAPI_APP, HAS_CONFLICTS_LABEL_STR, HOLD_LABEL_STR, IN_PROGRESS_STR, @@ -63,13 +64,16 @@ ) +LOGGER = get_logger(name="GitHubApi") + + class RepositoryNotFoundError(Exception): pass class GitHubApi: def __init__(self, hook_data): - self.app = FLASK_APP + self.app = FastAPI_APP self.hook_data = hook_data self.repository_name = hook_data["repository"]["name"] self.log_prefix_with_color = None @@ -108,7 +112,7 @@ def __init__(self, hook_data): self.github_app_api = get_repository_github_app_api(config=self.config, repository=self.repository_full_name) if not self.github_app_api: - FLASK_APP.logger.error( + LOGGER.error( f"Repository {self.repository_full_name} not found by manage-repositories-app, " f"make sure the app installed (https://github.com/apps/manage-repositories-app)" ) @@ -395,7 +399,7 @@ def skip_if_pull_request_already_merged(self): self.app.logger.info(f"{self.log_prefix}: PR is merged, not processing") return True - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def _remove_label(self, label): if self.label_exists_in_pull_request(label=label): self.app.logger.info(f"{self.log_prefix} Removing label {label}") @@ -404,7 +408,7 @@ def _remove_label(self, label): self.app.logger.warning(f"{self.log_prefix} Label {label} not found and cannot be removed") - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def _add_label(self, label): label = label.strip() if len(label) > 49: @@ -455,7 +459,7 @@ def _generate_issue_title(self): def _generate_issue_body(self): return f"[Auto generated]\nNumber: [#{self.pull_request.number}]" - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def is_branch_exists(self, branch): return self.repository.get_branch(branch) @@ -620,119 +624,119 @@ def reset_verify_label(self): # Remove verified label self._remove_label(label=VERIFIED_LABEL_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_verify_check_queued(self): return self.set_check_run_status(check_run=VERIFIED_LABEL_STR, status=QUEUED_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_verify_check_success(self): return self.set_check_run_status(check_run=VERIFIED_LABEL_STR, conclusion=SUCCESS_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_run_tox_check_queued(self): if not self.tox_enabled: return False return self.set_check_run_status(check_run=TOX_STR, status=QUEUED_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_run_tox_check_in_progress(self): return self.set_check_run_status(check_run=TOX_STR, status=IN_PROGRESS_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_run_tox_check_failure(self, output): return self.set_check_run_status(check_run=TOX_STR, conclusion=FAILURE_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_run_tox_check_success(self, output): return self.set_check_run_status(check_run=TOX_STR, conclusion=SUCCESS_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_run_pre_commit_check_queued(self): if not self.pre_commit: return False return self.set_check_run_status(check_run=PRE_COMMIT_STR, status=QUEUED_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_run_pre_commit_check_in_progress(self): return self.set_check_run_status(check_run=PRE_COMMIT_STR, status=IN_PROGRESS_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_run_pre_commit_check_failure(self, output): return self.set_check_run_status(check_run=PRE_COMMIT_STR, conclusion=FAILURE_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_run_pre_commit_check_success(self, output): return self.set_check_run_status(check_run=PRE_COMMIT_STR, conclusion=SUCCESS_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_merge_check_queued(self, output=None): return self.set_check_run_status(check_run=CAN_BE_MERGED_STR, status=QUEUED_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_merge_check_in_progress(self): return self.set_check_run_status(check_run=CAN_BE_MERGED_STR, status=IN_PROGRESS_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_merge_check_success(self): return self.set_check_run_status(check_run=CAN_BE_MERGED_STR, conclusion=SUCCESS_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_merge_check_failure(self, output): return self.set_check_run_status(check_run=CAN_BE_MERGED_STR, conclusion=FAILURE_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_container_build_queued(self): if not self.build_and_push_container: return return self.set_check_run_status(check_run=BUILD_CONTAINER_STR, status=QUEUED_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_container_build_in_progress(self): return self.set_check_run_status(check_run=BUILD_CONTAINER_STR, status=IN_PROGRESS_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_container_build_success(self, output): return self.set_check_run_status(check_run=BUILD_CONTAINER_STR, conclusion=SUCCESS_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_container_build_failure(self, output): return self.set_check_run_status(check_run=BUILD_CONTAINER_STR, conclusion=FAILURE_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_python_module_install_queued(self): if not self.pypi: return False return self.set_check_run_status(check_run=PYTHON_MODULE_INSTALL_STR, status=QUEUED_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_python_module_install_in_progress(self): return self.set_check_run_status(check_run=PYTHON_MODULE_INSTALL_STR, status=IN_PROGRESS_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_python_module_install_success(self, output): return self.set_check_run_status(check_run=PYTHON_MODULE_INSTALL_STR, conclusion=SUCCESS_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_python_module_install_failure(self, output): return self.set_check_run_status(check_run=PYTHON_MODULE_INSTALL_STR, conclusion=FAILURE_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_cherry_pick_in_progress(self): return self.set_check_run_status(check_run=CHERRY_PICKED_LABEL_PREFIX, status=IN_PROGRESS_STR) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_cherry_pick_success(self, output): return self.set_check_run_status(check_run=CHERRY_PICKED_LABEL_PREFIX, conclusion=SUCCESS_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def set_cherry_pick_failure(self, output): return self.set_check_run_status(check_run=CHERRY_PICKED_LABEL_PREFIX, conclusion=FAILURE_STR, output=output) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def create_issue_for_new_pull_request(self): if self.parent_committer in self.auto_verified_and_merged_users: self.app.logger.info( @@ -748,7 +752,7 @@ def create_issue_for_new_pull_request(self): assignee=self.pull_request.user.login, ) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def close_issue_for_merged_or_closed_pr(self, hook_action): for issue in self.repository.get_issues(): if issue.body == self._generate_issue_body(): @@ -759,7 +763,7 @@ def close_issue_for_merged_or_closed_pr(self, hook_action): issue.edit(state="closed") break - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def delete_remote_tag_for_merged_or_closed_pr(self, hook_action): pr_tag = f"pr-{self.pull_request.number}" # run regctl as a container: @@ -1230,7 +1234,7 @@ def user_commands(self, command, reviewed_user, issue_comment_id): issue_comment_id=issue_comment_id, ) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def cherry_pick(self, target_branch, reviewed_user=None): requested_by = reviewed_user or "by target-branch label" self.app.logger.info(f"{self.log_prefix} Cherry-pick requested by user: {requested_by}") @@ -1290,7 +1294,7 @@ def cherry_pick(self, target_branch, reviewed_user=None): "```" ) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def label_by_pull_requests_merge_state_after_merged(self): """ Labels pull requests based on their mergeable state. @@ -1480,7 +1484,7 @@ def _container_repository_and_tag(self, is_merged=None, tag=None): self.app.logger.error(f"{self.log_prefix} container tag not found") - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def _run_build_container( self, set_check=True, @@ -1719,7 +1723,7 @@ def get_checkrun_text(self, err, out): else: return f"```\n{err}\n\n{out}\n```" - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def get_jira_conn(self): self.jira_conn = JiraApi( server=self.jira_server, @@ -1760,7 +1764,7 @@ def get_story_key_with_jira_connection(self): return None return _story_key - @ignore_exceptions(logger=FLASK_APP.logger, return_on_error=[]) + @ignore_exceptions(logger=LOGGER, return_on_error=[]) def get_branch_required_status_checks(self): if self.repository.private: self.app.logger.info( diff --git a/webhook_server_container/libs/jira_api.py b/webhook_server_container/libs/jira_api.py index 8b3da57e..784c8a15 100644 --- a/webhook_server_container/libs/jira_api.py +++ b/webhook_server_container/libs/jira_api.py @@ -1,8 +1,10 @@ from typing import Any, Dict from jira import JIRA from pyhelper_utils.general import ignore_exceptions +from simple_logger.logger import get_logger -from webhook_server_container.utils.constants import FLASK_APP + +LOGGER = get_logger(name="JiraApi") class JiraApi: @@ -18,7 +20,7 @@ def __init__(self, server: str, project: str, token: str): self.conn.my_permissions() self.fields: Dict[str, Any] = {"project": {"key": self.project}} - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def create_story(self, title: str, body: str, epic_key: str, assignee: str) -> str: self.fields.update({ "summary": title, @@ -33,7 +35,7 @@ def create_story(self, title: str, body: str, epic_key: str, assignee: str) -> s _issue = self.conn.create_issue(fields=self.fields) return _issue.key - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def create_closed_subtask(self, title: str, body: str, parent_key: str, assignee: str) -> None: self.fields.update({ "summary": title, @@ -45,7 +47,7 @@ def create_closed_subtask(self, title: str, body: str, parent_key: str, assignee _issue = self.conn.create_issue(fields=self.fields) self.close_issue(key=_issue.key) - @ignore_exceptions(logger=FLASK_APP.logger) + @ignore_exceptions(logger=LOGGER) def close_issue(self, key: str, comment: str = "") -> None: self.conn.transition_issue( issue=key, diff --git a/webhook_server_container/libs/label.py b/webhook_server_container/libs/label.py new file mode 100644 index 00000000..3fb8d3fe --- /dev/null +++ b/webhook_server_container/libs/label.py @@ -0,0 +1,8 @@ +class Label: + def __init__(self, name, color, description): + self.name = name + self.color = color + self.description = description + + def __str__(self): + return f"Label(name={self.name}, color={self.color}, description={self.description})" diff --git a/webhook_server_container/utils/constants.py b/webhook_server_container/utils/constants.py index dde0e9d0..653db168 100644 --- a/webhook_server_container/utils/constants.py +++ b/webhook_server_container/utils/constants.py @@ -1,10 +1,7 @@ -from flask import Flask -from flask.logging import default_handler -from simple_logger.logger import get_logger +from fastapi import FastAPI -FLASK_APP = Flask("webhook-server") -FLASK_APP.logger.removeHandler(default_handler) -FLASK_APP.logger.addHandler(get_logger(FLASK_APP.logger.name).handlers[0]) + +FastAPI_APP = FastAPI(title="webhook-server") OTHER_MAIN_BRANCH = "master" diff --git a/webhook_server_container/utils/github_repository_settings.py b/webhook_server_container/utils/github_repository_settings.py index 44cb6731..e5f322d0 100644 --- a/webhook_server_container/utils/github_repository_settings.py +++ b/webhook_server_container/utils/github_repository_settings.py @@ -5,12 +5,12 @@ from github import GithubIntegration, Auth from github.GithubException import UnknownObjectException +from simple_logger.logger import get_logger from webhook_server_container.libs.config import Config from webhook_server_container.utils.constants import ( BUILD_CONTAINER_STR, CAN_BE_MERGED_STR, - FLASK_APP, IN_PROGRESS_STR, PRE_COMMIT_STR, PYTHON_MODULE_INSTALL_STR, @@ -25,17 +25,18 @@ ) -@ignore_exceptions(logger=FLASK_APP.logger) +LOGGER = get_logger(name="github-repository-settings") + + +@ignore_exceptions(logger=LOGGER) def get_branch_sampler(repo, branch_name): return repo.get_branch(branch=branch_name) -@ignore_exceptions(logger=FLASK_APP.logger) +@ignore_exceptions(logger=LOGGER) def set_branch_protection(branch, repository, required_status_checks, github_api): api_user = github_api.get_user().login - FLASK_APP.logger.info( - f"Set repository {repository.name} {branch} settings. enabled checks: {required_status_checks}" - ) + LOGGER.info(f"Set repository {repository.name} {branch} settings. enabled checks: {required_status_checks}") branch.edit_protection( strict=True, required_conversation_resolution=True, @@ -50,15 +51,15 @@ def set_branch_protection(branch, repository, required_status_checks, github_api ) -@ignore_exceptions(logger=FLASK_APP.logger) +@ignore_exceptions(logger=LOGGER) def set_repository_settings(repository): - FLASK_APP.logger.info(f"Set repository {repository.name} settings") + LOGGER.info(f"Set repository {repository.name} settings") repository.edit(delete_branch_on_merge=True, allow_auto_merge=True, allow_update_branch=True) - FLASK_APP.logger.info(f"Set repository {repository.name} security settings") + LOGGER.info(f"Set repository {repository.name} security settings") if repository.private: - FLASK_APP.logger.warning(f"{repository.name}: Repository is private, skipping setting security settings") + LOGGER.warning(f"{repository.name}: Repository is private, skipping setting security settings") return repository._requester.requestJsonAndCheck( @@ -114,7 +115,7 @@ def get_user_configures_status_checks(status_checks): def set_repository_labels(repository): - FLASK_APP.logger.info(f"Set repository {repository.name} labels") + LOGGER.info(f"Set repository {repository.name} labels") repository_labels = {} for label in repository.get_labels(): repository_labels[label.name.lower()] = {"object": label, "color": label.color} @@ -126,22 +127,22 @@ def set_repository_labels(repository): if repository_labels[label_lower]["color"] == color: continue else: - FLASK_APP.logger.info(f"{repository.name}: Edit repository label {label} with color {color}") + LOGGER.info(f"{repository.name}: Edit repository label {label} with color {color}") repo_label.edit(name=repo_label.name, color=color) else: - FLASK_APP.logger.info(f"{repository.name}: Add repository label {label} with color {color}") + LOGGER.info(f"{repository.name}: Add repository label {label} with color {color}") repository.create_label(name=label, color=color) return f"{repository}: Setting repository labels is done" def set_repositories_settings(config, github_api): - FLASK_APP.logger.info("Processing repositories") + LOGGER.info("Processing repositories") config_data = config.data default_status_checks = config_data.get("default-status-checks", []) docker = config_data.get("docker") if docker: - FLASK_APP.logger.info("Login in to docker.io") + LOGGER.info("Login in to docker.io") docker_username = docker["username"] docker_password = docker["password"] os.system(f"podman login -u {docker_username} -p {docker_password} docker.io") @@ -153,17 +154,17 @@ def set_repositories_settings(config, github_api): for result in as_completed(futures): if result.exception(): - FLASK_APP.logger.error(result.exception()) - FLASK_APP.logger.info(result.result()) + LOGGER.error(result.exception()) + LOGGER.info(result.result()) def set_repository(data, github_api, default_status_checks): repository = data["name"] - FLASK_APP.logger.info(f"Processing repository {repository}") + LOGGER.info(f"Processing repository {repository}") protected_branches = data.get("protected-branches", {}) repo = get_github_repo_api(github_api=github_api, repository=repository) if not repo: - FLASK_APP.logger.error(f"{repository}: Failed to get repository") + LOGGER.error(f"{repository}: Failed to get repository") return try: @@ -171,14 +172,14 @@ def set_repository(data, github_api, default_status_checks): set_repository_settings(repository=repo) if repo.private: - FLASK_APP.logger.warning(f"{repository}: Repository is private, skipping setting branch settings") + LOGGER.warning(f"{repository}: Repository is private, skipping setting branch settings") return for branch_name, status_checks in protected_branches.items(): - FLASK_APP.logger.info(f"{repository}: Getting branch {branch_name}") + LOGGER.info(f"{repository}: Getting branch {branch_name}") branch = get_branch_sampler(repo=repo, branch_name=branch_name) if not branch: - FLASK_APP.logger.error(f"{repository}: Failed to get branch {branch_name}") + LOGGER.error(f"{repository}: Failed to get branch {branch_name}") continue _default_status_checks = deepcopy(default_status_checks) @@ -201,7 +202,7 @@ def set_repository(data, github_api, default_status_checks): github_api=github_api, ) except UnknownObjectException: - FLASK_APP.logger.error(f"{repository}: Failed to get repository settings") + LOGGER.error(f"{repository}: Failed to get repository settings") return f"{repository}: Setting repository settings is done" @@ -229,8 +230,8 @@ def set_all_in_progress_check_runs_to_queued(config, github_api): for result in as_completed(futures): if result.exception(): - FLASK_APP.logger.error(result.exception()) - FLASK_APP.logger.info(result.result()) + LOGGER.error(result.exception()) + LOGGER.info(result.result()) def set_repository_check_runs_to_queued(config, data, github_api, check_runs): @@ -241,12 +242,12 @@ def set_repository_check_runs_to_queued(config, data, github_api, check_runs): app_api = get_github_repo_api(github_api=repository_app_api, repository=repository) repo = get_github_repo_api(github_api=github_api, repository=repository) - FLASK_APP.logger.info(f"{repository}: Set all {IN_PROGRESS_STR} check runs to {QUEUED_STR}") + LOGGER.info(f"{repository}: Set all {IN_PROGRESS_STR} check runs to {QUEUED_STR}") for pull_request in repo.get_pulls(state="open"): last_commit = list(pull_request.get_commits())[-1] for check_run in last_commit.get_check_runs(): if check_run.name in check_runs and check_run.status == IN_PROGRESS_STR: - FLASK_APP.logger.info( + LOGGER.info( f"{repository}: {check_run.name} status is {IN_PROGRESS_STR}, " f"Setting check run {check_run.name} to {QUEUED_STR}" ) @@ -255,9 +256,9 @@ def set_repository_check_runs_to_queued(config, data, github_api, check_runs): return f"{repository}: Set check run status to {QUEUED_STR} is done" -@ignore_exceptions(logger=FLASK_APP.logger) +@ignore_exceptions(logger=LOGGER) def get_repository_github_app_api(config, repository): - FLASK_APP.logger.info("Getting repositories GitHub app API") + LOGGER.info("Getting repositories GitHub app API") with open(os.path.join(config.data_dir, "webhook-server.private-key.pem")) as fd: private_key = fd.read() @@ -268,7 +269,7 @@ def get_repository_github_app_api(config, repository): try: return app_instance.get_repo_installation(owner=owner, repo=repo).get_github_for_installation() except UnknownObjectException: - FLASK_APP.logger.error( + LOGGER.error( f"Repository {repository} not found by manage-repositories-app, " f"make sure the app installed (https://github.com/apps/manage-repositories-app)" ) diff --git a/webhook_server_container/utils/helpers.py b/webhook_server_container/utils/helpers.py index 9f93faf4..03b17107 100644 --- a/webhook_server_container/utils/helpers.py +++ b/webhook_server_container/utils/helpers.py @@ -5,8 +5,10 @@ from pyhelper_utils.general import ignore_exceptions from colorama import Fore from github import Github +from simple_logger.logger import get_logger -from webhook_server_container.utils.constants import FLASK_APP + +LOGGER = get_logger(name="helpers") def extract_key_from_dict(key, _dict): @@ -23,7 +25,7 @@ def extract_key_from_dict(key, _dict): yield result -@ignore_exceptions(logger=FLASK_APP.logger) +@ignore_exceptions(logger=LOGGER) def get_github_repo_api(github_api, repository): return github_api.get_repo(repository) @@ -56,7 +58,7 @@ def run_command( """ out_decoded, err_decoded = "", "" try: - FLASK_APP.logger.info(f"{log_prefix} Running '{command}' command") + LOGGER.info(f"{log_prefix} Running '{command}' command") sub_process = subprocess.run( shlex.split(command), capture_output=capture_output, @@ -75,17 +77,17 @@ def run_command( ) if sub_process.returncode != 0: - FLASK_APP.logger.error(error_msg) + LOGGER.error(error_msg) return False, out_decoded, err_decoded # From this point and onwards we are guaranteed that sub_process.returncode == 0 if err_decoded and verify_stderr: - FLASK_APP.logger.error(error_msg) + LOGGER.error(error_msg) return False, out_decoded, err_decoded return True, out_decoded, err_decoded except Exception as ex: - FLASK_APP.logger.error(f"{log_prefix} Failed to run '{command}' command: {ex}") + LOGGER.error(f"{log_prefix} Failed to run '{command}' command: {ex}") return False, out_decoded, err_decoded @@ -105,7 +107,7 @@ def get_apis_and_tokes_from_config(config, repository_name=None): return apis_and_tokens -@ignore_exceptions(logger=FLASK_APP.logger) +@ignore_exceptions(logger=LOGGER) def get_api_with_highest_rate_limit(config, repository_name=None): """ Get API with the highest rate limit @@ -126,11 +128,11 @@ def get_api_with_highest_rate_limit(config, repository_name=None): rate_limit = _api.get_rate_limit() if rate_limit.core.remaining > remaining: remaining = rate_limit.core.remaining - FLASK_APP.logger.info(f"API user {_api_user} remaining rate limit: {remaining}") + LOGGER.info(f"API user {_api_user} remaining rate limit: {remaining}") api, token = _api, _token log_rate_limit(rate_limit=rate_limit, api_user=_api_user) - FLASK_APP.logger.info(f"API user {_api_user} selected with highest rate limit: {remaining}") + LOGGER.info(f"API user {_api_user} selected with highest rate limit: {remaining}") return api, token @@ -142,7 +144,7 @@ def log_rate_limit(rate_limit, api_user): rate_limit_str = f"{Fore.YELLOW}{rate_limit.core.remaining}{Fore.RESET}" else: rate_limit_str = f"{Fore.GREEN}{rate_limit.core.remaining}{Fore.RESET}" - FLASK_APP.logger.info( + LOGGER.info( f"{Fore.CYAN}[{api_user}] API rate limit:{Fore.RESET} Current {rate_limit_str} of {rate_limit.core.limit}. " f"Reset in {rate_limit.core.reset} [{datetime.timedelta(seconds=time_for_limit_reset)}] " f"(UTC time is {datetime.datetime.now(tz=datetime.timezone.utc)})" diff --git a/webhook_server_container/utils/webhook.py b/webhook_server_container/utils/webhook.py index b289e8e9..95cfd63d 100644 --- a/webhook_server_container/utils/webhook.py +++ b/webhook_server_container/utils/webhook.py @@ -1,18 +1,21 @@ from concurrent.futures import ThreadPoolExecutor, as_completed +from simple_logger.logger import get_logger from webhook_server_container.libs.config import Config -from webhook_server_container.utils.constants import FLASK_APP from webhook_server_container.utils.helpers import get_api_with_highest_rate_limit, get_github_repo_api from pyhelper_utils.general import ignore_exceptions -@ignore_exceptions(logger=FLASK_APP.logger) +LOGGER = get_logger(name="webhook") + + +@ignore_exceptions(logger=LOGGER) def process_github_webhook(data, github_api, webhook_ip): repository = data["name"] repo = get_github_repo_api(github_api=github_api, repository=repository) if not repo: - FLASK_APP.logger.error(f"Could not find repository {repository}") + LOGGER.error(f"Could not find repository {repository}") return config = {"url": f"{webhook_ip}/webhook_server", "content_type": "json"} @@ -21,21 +24,21 @@ def process_github_webhook(data, github_api, webhook_ip): try: hooks = list(repo.get_hooks()) except Exception as ex: - FLASK_APP.logger.error(f"Could not list webhook for {repository}, check token permissions: {ex}") + LOGGER.error(f"Could not list webhook for {repository}, check token permissions: {ex}") return for _hook in hooks: if webhook_ip in _hook.config["url"]: - FLASK_APP.logger.info(f"webhook already exists, not creating new one: {repository}: {_hook.config['url']}") + LOGGER.info(f"webhook already exists, not creating new one: {repository}: {_hook.config['url']}") return f"{repository}: Hook already exists" - FLASK_APP.logger.info(f"Creating webhook: {config['url']} for {repository} with events: {events}") + LOGGER.info(f"Creating webhook: {config['url']} for {repository} with events: {events}") repo.create_hook(name="web", config=config, events=events, active=True) return f"{repository}: Create webhook is done" def create_webhook(config, github_api): - FLASK_APP.logger.info("Preparing webhook configuration") + LOGGER.info("Preparing webhook configuration") webhook_ip = config.data["webhook_ip"] futures = [] @@ -45,8 +48,8 @@ def create_webhook(config, github_api): for result in as_completed(futures): if result.exception(): - FLASK_APP.logger.error(result.exception()) - FLASK_APP.logger.info(result.result()) + LOGGER.error(result.exception()) + LOGGER.info(result.result()) if __name__ == "__main__": From 678021fb153002385a6fd66ba2699f0397f8eccb Mon Sep 17 00:00:00 2001 From: rnetser Date: Tue, 25 Jun 2024 15:03:55 +0300 Subject: [PATCH 2/7] update post function --- webhook_server_container/app.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/webhook_server_container/app.py b/webhook_server_container/app.py index 8a8d0764..75c460b5 100644 --- a/webhook_server_container/app.py +++ b/webhook_server_container/app.py @@ -1,6 +1,5 @@ import requests import urllib3 -from flask import request from simple_logger.logger import get_logger from webhook_server_container.libs.github_api import GitHubApi @@ -22,7 +21,7 @@ def healthcheck(): @FastAPI_APP.post(APP_ROOT_PATH) -async def process_webhook(): +def process_webhook(request): process_failed_msg = {"status": requests.status_codes.codes.server_error, "Message": "Process failed"} try: hook_data = request.json From 99dde9df83a8bea821b26b9149951df4b7c4a516 Mon Sep 17 00:00:00 2001 From: rnetser Date: Tue, 25 Jun 2024 15:12:04 +0300 Subject: [PATCH 3/7] remove label module --- webhook_server_container/libs/label.py | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 webhook_server_container/libs/label.py diff --git a/webhook_server_container/libs/label.py b/webhook_server_container/libs/label.py deleted file mode 100644 index 3fb8d3fe..00000000 --- a/webhook_server_container/libs/label.py +++ /dev/null @@ -1,8 +0,0 @@ -class Label: - def __init__(self, name, color, description): - self.name = name - self.color = color - self.description = description - - def __str__(self): - return f"Label(name={self.name}, color={self.color}, description={self.description})" From 9d5f588105fa9b2dc44ecb0b5b4465232e9986bd Mon Sep 17 00:00:00 2001 From: rnetser Date: Tue, 25 Jun 2024 16:16:51 +0300 Subject: [PATCH 4/7] add missing host and port --- entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entrypoint.sh b/entrypoint.sh index 23ebbdea..d1b0ca3f 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash -SERVER_RUN_CMD="poetry run uvicorn webhook_server_container.app:FastAPI_APP " +SERVER_RUN_CMD="poetry run uvicorn webhook_server_container.app:FastAPI_APP --host 0.0.0.0 --port 5000 " UVICORN_WORKERS="${UVICORN_MAX_WORKERS:=10}" set -ep From 45be3e8f16160b96180c27614221fa797752c9c9 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 27 Jun 2024 16:16:05 +0300 Subject: [PATCH 5/7] Fix request type --- webhook_server_container/app.py | 17 +++++++++++------ webhook_server_container/utils/constants.py | 1 - 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/webhook_server_container/app.py b/webhook_server_container/app.py index 75c460b5..656628ae 100644 --- a/webhook_server_container/app.py +++ b/webhook_server_container/app.py @@ -1,13 +1,14 @@ +from fastapi import Request import requests import urllib3 from simple_logger.logger import get_logger +from pydantic import BaseModel + from webhook_server_container.libs.github_api import GitHubApi -from webhook_server_container.utils.constants import ( - APP_ROOT_PATH, - FastAPI_APP, -) +from webhook_server_container.utils.constants import FastAPI_APP +APP_ROOT_PATH = "/webhook_server" REPOSITORIES_APP_API = {} MISSING_APP_REPOSITORIES = [] urllib3.disable_warnings() @@ -15,16 +16,20 @@ LOGGER = get_logger(name="app") +class Payload(BaseModel): + data: str = "" + + @FastAPI_APP.get(f"{APP_ROOT_PATH}/healthcheck") def healthcheck(): return {"status": requests.status_codes.codes.ok, "message": "Alive"} @FastAPI_APP.post(APP_ROOT_PATH) -def process_webhook(request): +async def process_webhook(request: Request): process_failed_msg = {"status": requests.status_codes.codes.server_error, "Message": "Process failed"} try: - hook_data = request.json + hook_data = await request.json() except Exception as ex: LOGGER.error(f"Error get JSON from request: {ex}") return process_failed_msg diff --git a/webhook_server_container/utils/constants.py b/webhook_server_container/utils/constants.py index 653db168..d0ed7a5e 100644 --- a/webhook_server_container/utils/constants.py +++ b/webhook_server_container/utils/constants.py @@ -5,7 +5,6 @@ OTHER_MAIN_BRANCH = "master" -APP_ROOT_PATH = "/webhook_server" TOX_STR = "tox" PRE_COMMIT_STR = "pre-commit" BUILD_AND_PUSH_CONTAINER_STR = "build-and-push-container" From 749855bfec94dfbbef8c331abdc73d8b778d4fe8 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 27 Jun 2024 16:19:45 +0300 Subject: [PATCH 6/7] remove uneeded code --- webhook_server_container/app.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/webhook_server_container/app.py b/webhook_server_container/app.py index 656628ae..1f2b49f6 100644 --- a/webhook_server_container/app.py +++ b/webhook_server_container/app.py @@ -2,7 +2,6 @@ import requests import urllib3 from simple_logger.logger import get_logger -from pydantic import BaseModel from webhook_server_container.libs.github_api import GitHubApi @@ -16,10 +15,6 @@ LOGGER = get_logger(name="app") -class Payload(BaseModel): - data: str = "" - - @FastAPI_APP.get(f"{APP_ROOT_PATH}/healthcheck") def healthcheck(): return {"status": requests.status_codes.codes.ok, "message": "Alive"} From 288ac0bb16ecdbe45226da4cd1954298ebe95eca Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 27 Jun 2024 16:23:06 +0300 Subject: [PATCH 7/7] Fix logger calls --- webhook_server_container/libs/github_api.py | 214 ++++++++++---------- 1 file changed, 102 insertions(+), 112 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index c03ef6c7..f6765308 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -127,7 +127,7 @@ def __init__(self, hook_data): github_api=self.github_app_api, repository=self.repository_full_name ) if not (self.repository or self.repository_by_github_app): - self.app.logger.error(f"{self.log_prefix} Failed to get repository.") + LOGGER.error(f"{self.log_prefix} Failed to get repository.") return self.add_api_users_to_auto_verified_and_merged_users() @@ -149,18 +149,16 @@ def __init__(self, hook_data): if self.parent_committer in reviewers_and_approvers: self.jira_assignee = self.jira_user_mapping.get(self.parent_committer) if not self.jira_assignee: - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} Jira tracking is disabled for the current pull request. " f"Committer {self.parent_committer} is not in configures in jira-user-mapping" ) else: self.jira_track_pr = True self.issue_title = f"[AUTO:FROM:GITHUB] [{self.repository_name}] PR [{self.pull_request.number}]: {self.pull_request.title}" - self.app.logger.info( - f"{self.log_prefix} Jira tracking is enabled for the current pull request." - ) + LOGGER.info(f"{self.log_prefix} Jira tracking is enabled for the current pull request.") else: - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} Jira tracking is disabled for the current pull request. " f"Committer {self.parent_committer} is not in {reviewers_and_approvers}" ) @@ -250,14 +248,14 @@ def hash_token(self, message): def app_logger_info(self, message): hashed_message = self.hash_token(message=message) - self.app.logger.info(hashed_message) + LOGGER.info(hashed_message) def app_logger_error(self, message): hashed_message = self.hash_token(message=message) - self.app.logger.error(hashed_message) + LOGGER.error(hashed_message) def process_hook(self, data, event_log): - self.app.logger.info(f"{self.log_prefix} {event_log}") + LOGGER.info(f"{self.log_prefix} {event_log}") if data == "ping": return @@ -280,7 +278,7 @@ def process_pull_request_check_run_webhook_data(self): _check_run = self.hook_data["check_run"] check_run_name = _check_run["name"] if check_run_name == CAN_BE_MERGED_STR: - self.app.logger.info(f"{self.log_prefix} check_run '{check_run_name}' skipped") + LOGGER.info(f"{self.log_prefix} check_run '{check_run_name}' skipped") return if ( @@ -288,7 +286,7 @@ def process_pull_request_check_run_webhook_data(self): and _check_run["conclusion"] == SUCCESS_STR and check_run_name in self.all_required_status_checks ): - self.app.logger.info(f"{self.log_prefix} check_run '{check_run_name}' completed and {SUCCESS_STR}") + LOGGER.info(f"{self.log_prefix} check_run '{check_run_name}' completed and {SUCCESS_STR}") for _pull_request in self.repository.get_pulls(state="open"): _last_commit = list(_pull_request.get_commits())[-1] for _commit_check_run in _last_commit.get_check_runs(): @@ -342,7 +340,7 @@ def _repo_data_from_config(self): self.jira_enabled_repository = all([self.jira_server, self.jira_project, self.jira_token]) if not self.jira_enabled_repository: # if not (self.jira_enabled_repository := all([self.jira_server, self.jira_project, self.jira_token])): - self.app.logger.error( + LOGGER.error( f"{self.log_prefix} Jira configuration is not valid. Server: {self.jira_server}, Project: {self.jira_project}, Token: {self.jira_token}" ) @@ -383,7 +381,7 @@ def _get_pull_request(self, number=None): with contextlib.suppress(Exception): return commit_obj.get_pulls()[0] - self.app.logger.info(f"{self.log_prefix} No issue or pull_request found in hook data") + LOGGER.info(f"{self.log_prefix} No issue or pull_request found in hook data") def _get_last_commit(self): return list(self.pull_request.get_commits())[-1] @@ -396,47 +394,47 @@ def pull_request_labels_names(self): def skip_if_pull_request_already_merged(self): if self.pull_request.is_merged(): - self.app.logger.info(f"{self.log_prefix}: PR is merged, not processing") + LOGGER.info(f"{self.log_prefix}: PR is merged, not processing") return True @ignore_exceptions(logger=LOGGER) def _remove_label(self, label): if self.label_exists_in_pull_request(label=label): - self.app.logger.info(f"{self.log_prefix} Removing label {label}") + LOGGER.info(f"{self.log_prefix} Removing label {label}") self.pull_request.remove_from_labels(label) return self.wait_for_label(label=label, exists=False) - self.app.logger.warning(f"{self.log_prefix} Label {label} not found and cannot be removed") + LOGGER.warning(f"{self.log_prefix} Label {label} not found and cannot be removed") @ignore_exceptions(logger=LOGGER) def _add_label(self, label): label = label.strip() if len(label) > 49: - self.app.logger.warning(f"{label} is to long, not adding.") + LOGGER.warning(f"{label} is to long, not adding.") return if self.label_exists_in_pull_request(label=label): - self.app.logger.info(f"{self.log_prefix} Label {label} already assign to PR {self.pull_request.number}") + LOGGER.info(f"{self.log_prefix} Label {label} already assign to PR {self.pull_request.number}") return if label in STATIC_LABELS_DICT: - self.app.logger.info(f"{self.log_prefix} Adding pull request label {label} to {self.pull_request.number}") + LOGGER.info(f"{self.log_prefix} Adding pull request label {label} to {self.pull_request.number}") return self.pull_request.add_to_labels(label) _color = [DYNAMIC_LABELS_DICT[_label] for _label in DYNAMIC_LABELS_DICT if _label in label] - self.app.logger.info(f"{self.log_prefix} Label {label} was {'found' if _color else 'not found'} in labels dict") + LOGGER.info(f"{self.log_prefix} Label {label} was {'found' if _color else 'not found'} in labels dict") color = _color[0] if _color else "D4C5F9" - self.app.logger.info(f"{self.log_prefix} Adding label {label} with color {color}") + LOGGER.info(f"{self.log_prefix} Adding label {label} with color {color}") try: _repo_label = self.repository.get_label(label) _repo_label.edit(name=_repo_label.name, color=color) - self.app.logger.info(f"{self.log_prefix} Edit repository label {label} with color {color}") + LOGGER.info(f"{self.log_prefix} Edit repository label {label} with color {color}") except UnknownObjectException: - self.app.logger.info(f"{self.log_prefix} Add repository label {label} with color {color}") + LOGGER.info(f"{self.log_prefix} Add repository label {label} with color {color}") self.repository.create_label(name=label, color=color) - self.app.logger.info(f"{self.log_prefix} Adding pull request label {label} to {self.pull_request.number}") + LOGGER.info(f"{self.log_prefix} Adding pull request label {label} to {self.pull_request.number}") self.pull_request.add_to_labels(label) return self.wait_for_label(label=label, exists=True) @@ -451,7 +449,7 @@ def wait_for_label(self, label, exists): if sample == exists: return True except TimeoutExpiredError: - self.app.logger.warning(f"{self.log_prefix} Label {label} {'not found' if exists else 'found'}") + LOGGER.warning(f"{self.log_prefix} Label {label} {'not found' if exists else 'found'}") def _generate_issue_title(self): return f"{self.pull_request.title} - {self.pull_request.number}" @@ -467,7 +465,7 @@ def upload_to_pypi(self, tag_name): out, err = "", "" token = self.pypi["token"] env = f"-e TWINE_USERNAME=__token__ -e TWINE_PASSWORD={token} " - self.app.logger.info(f"{self.log_prefix} Start uploading to pypi") + LOGGER.info(f"{self.log_prefix} Start uploading to pypi") _dist_dir = "/tmp/dist" cmd = ( f" python3 -m build --sdist --outdir {_dist_dir} ." @@ -477,7 +475,7 @@ def upload_to_pypi(self, tag_name): try: rc, out, err = self._run_in_container(command=cmd, env=env, checkout=tag_name) if rc: - self.app.logger.info(f"{self.log_prefix} Publish to pypi finished") + LOGGER.info(f"{self.log_prefix} Publish to pypi finished") if self.slack_webhook_url: message = f""" ``` @@ -488,7 +486,7 @@ def upload_to_pypi(self, tag_name): except Exception as exp: err = f"Publish to pypi failed: {exp}" - self.app.logger.error(f"{self.log_prefix} {err}") + LOGGER.error(f"{self.log_prefix} {err}") self.repository.create_issue( title=err, assignee=self.approvers[0] if self.approvers else None, @@ -502,10 +500,10 @@ def get_owners_content(self): try: owners_content = self.repository.get_contents("OWNERS") _content = yaml.safe_load(owners_content.decoded_content) - self.app.logger.info(f"{self.log_prefix} OWNERS file content: {_content}") + LOGGER.info(f"{self.log_prefix} OWNERS file content: {_content}") return _content except UnknownObjectException: - self.app.logger.error(f"{self.log_prefix} OWNERS file not found") + LOGGER.error(f"{self.log_prefix} OWNERS file not found") return {} @property @@ -516,7 +514,7 @@ def reviewers(self): else: _reviewers = bc_reviewers - self.app.logger.info(f"{self.log_prefix} Reviewers: {_reviewers}") + LOGGER.info(f"{self.log_prefix} Reviewers: {_reviewers}") return _reviewers @property @@ -541,7 +539,7 @@ def list_changed_commit_files(self): return [fd["filename"] for fd in self.last_commit.raw_data["files"]] def assign_reviewers(self): - self.app.logger.info(f"{self.log_prefix} Assign reviewers") + LOGGER.info(f"{self.log_prefix} Assign reviewers") changed_files = self.list_changed_commit_files() reviewers_to_add = self.reviewers for _file, _reviewers in self.files_reviewers.items(): @@ -553,14 +551,14 @@ def assign_reviewers(self): reviewers_to_add.extend(_reviewers) _to_add = list(set(reviewers_to_add)) - self.app.logger.info(f"{self.log_prefix} Reviewers to add: {_to_add}") + LOGGER.info(f"{self.log_prefix} Reviewers to add: {_to_add}") for reviewer in _to_add: if reviewer != self.pull_request.user.login: - self.app.logger.info(f"{self.log_prefix} Adding reviewer {reviewer}") + LOGGER.info(f"{self.log_prefix} Adding reviewer {reviewer}") try: self.pull_request.create_review_request([reviewer]) except GithubException as ex: - self.app.logger.error(f"{self.log_prefix} Failed to add reviewer {reviewer}. {ex}") + LOGGER.error(f"{self.log_prefix} Failed to add reviewer {reviewer}. {ex}") def add_size_label(self): size = self.pull_request.additions + self.pull_request.deletions @@ -596,13 +594,11 @@ def add_size_label(self): def label_by_user_comment(self, user_request, remove, reviewed_user, issue_comment_id): if not any(user_request.startswith(label_name) for label_name in USER_LABELS_DICT): - self.app.logger.info( - f"{self.log_prefix} Label {user_request} is not a predefined one, will not be added / removed." - ) + LOGGER.info(f"{self.log_prefix} Label {user_request} is not a predefined one, will not be added / removed.") return - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} {'Remove' if remove else 'Add'} " f"label requested by user {reviewed_user}: {user_request}" ) @@ -620,7 +616,7 @@ def label_by_user_comment(self, user_request, remove, reviewed_user, issue_comme label_func(label=user_request) def reset_verify_label(self): - self.app.logger.info(f"{self.log_prefix} Processing reset {VERIFIED_LABEL_STR} label on new commit push") + LOGGER.info(f"{self.log_prefix} Processing reset {VERIFIED_LABEL_STR} label on new commit push") # Remove verified label self._remove_label(label=VERIFIED_LABEL_STR) @@ -739,13 +735,13 @@ def set_cherry_pick_failure(self, output): @ignore_exceptions(logger=LOGGER) def create_issue_for_new_pull_request(self): if self.parent_committer in self.auto_verified_and_merged_users: - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} Committer {self.parent_committer} is part of " f"{self.auto_verified_and_merged_users}, will not create issue." ) return - self.app.logger.info(f"{self.log_prefix} Creating issue for new PR: {self.pull_request.title}") + LOGGER.info(f"{self.log_prefix} Creating issue for new PR: {self.pull_request.title}") self.repository.create_issue( title=self._generate_issue_title(), body=self._generate_issue_body(), @@ -756,7 +752,7 @@ def create_issue_for_new_pull_request(self): def close_issue_for_merged_or_closed_pr(self, hook_action): for issue in self.repository.get_issues(): if issue.body == self._generate_issue_body(): - self.app.logger.info(f"{self.log_prefix} Closing issue {issue.title} for PR: {self.pull_request.title}") + LOGGER.info(f"{self.log_prefix} Closing issue {issue.title} for PR: {self.pull_request.title}") issue.create_comment( f"{self.log_prefix} Closing issue for PR: {self.pull_request.title}.\nPR was {hook_action}." ) @@ -801,7 +797,7 @@ def process_comment_webhook_data(self): return issue_number = self.hook_data["issue"]["number"] - self.app.logger.info(f"{self.log_prefix} Processing issue {issue_number}") + LOGGER.info(f"{self.log_prefix} Processing issue {issue_number}") if not self.pull_request: return @@ -809,9 +805,7 @@ def process_comment_webhook_data(self): body = self.hook_data["comment"]["body"] if body == self.welcome_msg: - self.app.logger.info( - f"{self.log_prefix} Welcome message found in issue {self.pull_request.title}. Not processing" - ) + LOGGER.info(f"{self.log_prefix} Welcome message found in issue {self.pull_request.title}. Not processing") return striped_body = body.strip() @@ -831,7 +825,7 @@ def process_comment_webhook_data(self): def process_pull_request_webhook_data(self): hook_action = self.hook_data["action"] - self.app.logger.info(f"{self.log_prefix} hook_action is: {hook_action}") + LOGGER.info(f"{self.log_prefix} hook_action is: {hook_action}") if not self.pull_request: return @@ -840,17 +834,17 @@ def process_pull_request_webhook_data(self): self.pull_request_branch = pull_request_data["base"]["ref"] if hook_action == "opened": - self.app.logger.info(f"{self.log_prefix} Creating welcome comment") + LOGGER.info(f"{self.log_prefix} Creating welcome comment") self.pull_request.create_issue_comment(self.welcome_msg) self.create_issue_for_new_pull_request() if self.jira_track_pr: self.get_jira_conn() if not self.jira_conn: - self.app.logger.error(f"{self.log_prefix} Jira connection not found") + LOGGER.error(f"{self.log_prefix} Jira connection not found") return - self.app.logger.info(f"{self.log_prefix} Creating Jira story") + LOGGER.info(f"{self.log_prefix} Creating Jira story") jira_story_key = self.jira_conn.create_story( title=self.issue_title, body=self.pull_request.html_url, @@ -873,7 +867,7 @@ def process_pull_request_webhook_data(self): if self.jira_track_pr: if _story_key := self.get_story_key_with_jira_connection(): - self.app.logger.info(f"{self.log_prefix} Creating sub-task for Jira story {_story_key}") + LOGGER.info(f"{self.log_prefix} Creating sub-task for Jira story {_story_key}") self.jira_conn.create_closed_subtask( title=f"{self.issue_title}: New commit from {self.last_committer}", parent_key=_story_key, @@ -890,13 +884,13 @@ def process_pull_request_webhook_data(self): if self.jira_track_pr: if _story_key := self.get_story_key_with_jira_connection(): - self.app.logger.info(f"{self.log_prefix} Closing Jira story") + LOGGER.info(f"{self.log_prefix} Closing Jira story") self.jira_conn.close_issue( key=_story_key, comment=f"PR: {self.pull_request.title} is closed. Megred: {is_merged}" ) if is_merged: - self.app.logger.info(f"{self.log_prefix} PR is merged") + LOGGER.info(f"{self.log_prefix} PR is merged") for _label in self.pull_request.labels: _label_name = _label.name @@ -920,7 +914,7 @@ def process_pull_request_webhook_data(self): if labeled == CAN_BE_MERGED_STR: return - self.app.logger.info(f"{self.log_prefix} PR {self.pull_request.number} {hook_action} with {labeled}") + LOGGER.info(f"{self.log_prefix} PR {self.pull_request.number} {hook_action} with {labeled}") if self.verified_job and labeled == VERIFIED_LABEL_STR: if action_labeled: self.set_verify_check_success() @@ -933,13 +927,13 @@ def process_push_webhook_data(self): tag = re.search(r"refs/tags/?(.*)", self.hook_data["ref"]) if tag: tag_name = tag.group(1) - self.app.logger.info(f"{self.log_prefix} Processing push for tag: {tag.group(1)}") + LOGGER.info(f"{self.log_prefix} Processing push for tag: {tag.group(1)}") if self.pypi: - self.app.logger.info(f"{self.log_prefix} Processing upload to pypi for tag: {tag_name}") + LOGGER.info(f"{self.log_prefix} Processing upload to pypi for tag: {tag_name}") self.upload_to_pypi(tag_name=tag_name) if self.container_release: - self.app.logger.info(f"{self.log_prefix} Processing build and push container for tag: {tag_name}") + LOGGER.info(f"{self.log_prefix} Processing build and push container for tag: {tag_name}") self._run_build_container(push=True, set_check=False, tag=tag_name) def process_pull_request_review_webhook_data(self): @@ -965,7 +959,7 @@ def process_pull_request_review_webhook_data(self): _story_label = [_label for _label in self.pull_request.labels if _label.name.startswith(JIRA_STR)] if _story_label: if reviewed_user == self.parent_committer or reviewed_user == self.last_committer: - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} Skipping Jira review sub-task creation for review by {reviewed_user} which is parent or last committer" ) return @@ -973,10 +967,10 @@ def process_pull_request_review_webhook_data(self): _story_key = _story_label[0].name.split(":")[-1] self.get_jira_conn() if not self.jira_conn: - self.app.logger.error(f"{self.log_prefix} Jira connection not found") + LOGGER.error(f"{self.log_prefix} Jira connection not found") return - self.app.logger.info(f"{self.log_prefix} Creating sub-task for Jira story {_story_key}") + LOGGER.info(f"{self.log_prefix} Creating sub-task for Jira story {_story_key}") self.jira_conn.create_closed_subtask( title=f"{self.issue_title}: reviewed by: {reviewed_user} - {review_state}", parent_key=_story_key, @@ -985,7 +979,7 @@ def process_pull_request_review_webhook_data(self): ) def manage_reviewed_by_label(self, review_state, action, reviewed_user): - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} " f"Processing label for review from {reviewed_user}. " f"review_state: {review_state}, action: {action}" @@ -1000,7 +994,7 @@ def manage_reviewed_by_label(self, review_state, action, reviewed_user): base_dict = self.hook_data.get("issue", self.hook_data.get("pull_request")) pr_owner = base_dict["user"]["login"] if pr_owner == reviewed_user: - self.app.logger.info(f"{self.log_prefix} PR owner {pr_owner} set /lgtm, not adding label.") + LOGGER.info(f"{self.log_prefix} PR owner {pr_owner} set /lgtm, not adding label.") return label_prefix = APPROVED_BY_LABEL_PREFIX @@ -1032,7 +1026,7 @@ def manage_reviewed_by_label(self, review_state, action, reviewed_user): if action == DELETE_STR: self._remove_label(label=reviewer_label) else: - self.app.logger.warning( + LOGGER.warning( f"{self.log_prefix} PR {self.pull_request.number} got unsupported review state: {review_state}" ) @@ -1041,7 +1035,7 @@ def _run_tox(self): return False if self.is_check_run_in_progress(check_run=TOX_STR): - self.app.logger.info(f"{self.log_prefix} Check run is in progress, not running {TOX_STR}.") + LOGGER.info(f"{self.log_prefix} Check run is in progress, not running {TOX_STR}.") return False cmd = f"{self.tox_python_version} -m {TOX_STR}" @@ -1067,7 +1061,7 @@ def _run_pre_commit(self): return False if self.is_check_run_in_progress(check_run=PRE_COMMIT_STR): - self.app.logger.info(f"{self.log_prefix} Check run is in progress, not running {PRE_COMMIT_STR}.") + LOGGER.info(f"{self.log_prefix} Check run is in progress, not running {PRE_COMMIT_STR}.") return False cmd = f"{PRE_COMMIT_STR} run --all-files" @@ -1093,23 +1087,23 @@ def user_commands(self, command, reviewed_user, issue_comment_id): "check-can-merge", ] if "sonarsource.github.io" in command: - self.app.logger.info(f"{self.log_prefix} command is in ignore list") + LOGGER.info(f"{self.log_prefix} command is in ignore list") return - self.app.logger.info(f"{self.log_prefix} Processing label/user command {command} by user {reviewed_user}") + LOGGER.info(f"{self.log_prefix} Processing label/user command {command} by user {reviewed_user}") command_and_args = command.split(" ", 1) _command = command_and_args[0] not_running_msg = f"Pull request already merged, not running {_command}" _args = command_and_args[1] if len(command_and_args) > 1 else "" if len(command_and_args) > 1 and _args == "cancel": - self.app.logger.info(f"{self.log_prefix} User requested 'cancel' for command {_command}") + LOGGER.info(f"{self.log_prefix} User requested 'cancel' for command {_command}") remove = True if _command in available_commands: if not _args and _command not in ("assign-reviewers", "check-can-merge"): issue_msg = f"{_command} requires an argument" error_msg = f"{self.log_prefix} {issue_msg}" - self.app.logger.info(error_msg) + LOGGER.info(error_msg) self.pull_request.create_issue_comment(issue_msg) return @@ -1136,7 +1130,7 @@ def user_commands(self, command, reviewed_user, issue_comment_id): _exits_target_branches.add(_target_branch) if _non_exits_target_branches_msg: - self.app.logger.info(f"{self.log_prefix} {_non_exits_target_branches_msg}") + LOGGER.info(f"{self.log_prefix} {_non_exits_target_branches_msg}") self.pull_request.create_issue_comment(_non_exits_target_branches_msg) if _exits_target_branches: @@ -1148,7 +1142,7 @@ def user_commands(self, command, reviewed_user, issue_comment_id): Cherry-pick requested for PR: `{self.pull_request.title}` by user `{reviewed_user}` Adding label/s `{" ".join([_cp_label for _cp_label in cp_labels])}` for automatic cheery-pick once the PR is merged """ - self.app.logger.info(f"{self.log_prefix} {info_msg}") + LOGGER.info(f"{self.log_prefix} {info_msg}") self.pull_request.create_issue_comment(info_msg) for _cp_label in cp_labels: self._add_label(label=_cp_label) @@ -1169,7 +1163,7 @@ def user_commands(self, command, reviewed_user, issue_comment_id): if not self.tox_enabled: msg = f"No {TOX_STR} configured for this repository" error_msg = f"{self.log_prefix} {msg}." - self.app.logger.info(error_msg) + LOGGER.info(error_msg) self.pull_request.create_issue_comment(msg) return @@ -1187,13 +1181,13 @@ def user_commands(self, command, reviewed_user, issue_comment_id): else: msg = f"No {BUILD_CONTAINER_STR} configured for this repository" error_msg = f"{self.log_prefix} {msg}" - self.app.logger.info(error_msg) + LOGGER.info(error_msg) self.pull_request.create_issue_comment(msg) elif _test == PYTHON_MODULE_INSTALL_STR: if not self.pypi: error_msg = f"{self.log_prefix} No pypi configured" - self.app.logger.info(error_msg) + LOGGER.info(error_msg) self.pull_request.create_issue_comment(error_msg) return @@ -1207,7 +1201,7 @@ def user_commands(self, command, reviewed_user, issue_comment_id): else: msg = f"No {BUILD_AND_PUSH_CONTAINER_STR} configured for this repository" error_msg = f"{self.log_prefix} {msg}" - self.app.logger.info(error_msg) + LOGGER.info(error_msg) self.pull_request.create_issue_comment(msg) elif _command == WIP_STR: @@ -1237,12 +1231,12 @@ def user_commands(self, command, reviewed_user, issue_comment_id): @ignore_exceptions(logger=LOGGER) def cherry_pick(self, target_branch, reviewed_user=None): requested_by = reviewed_user or "by target-branch label" - self.app.logger.info(f"{self.log_prefix} Cherry-pick requested by user: {requested_by}") + LOGGER.info(f"{self.log_prefix} Cherry-pick requested by user: {requested_by}") new_branch_name = f"{CHERRY_PICKED_LABEL_PREFIX}-{self.pull_request.head.ref}-{shortuuid.uuid()[:5]}" if not self.is_branch_exists(branch=target_branch): err_msg = f"cherry-pick failed: {target_branch} does not exists" - self.app.logger.error(err_msg) + LOGGER.error(err_msg) self.pull_request.create_issue_comment(err_msg) else: self.set_cherry_pick_in_progress() @@ -1278,7 +1272,7 @@ def cherry_pick(self, target_branch, reviewed_user=None): ) else: self.set_cherry_pick_failure(output=output) - self.app.logger.error(f"{self.log_prefix} Cherry pick failed: {out} --- {err}") + LOGGER.error(f"{self.log_prefix} Cherry pick failed: {out} --- {err}") local_branch_name = f"{self.pull_request.head.ref}-{target_branch}" self.pull_request.create_issue_comment( f"**Manual cherry-pick is needed**\nCherry pick failed for " @@ -1303,21 +1297,21 @@ def label_by_pull_requests_merge_state_after_merged(self): If the mergeable state is 'dirty', the 'has conflicts' label is added. """ time_sleep = 30 - self.app.logger.info(f"{self.log_prefix} Sleep for {time_sleep} seconds before getting all opened PRs") + LOGGER.info(f"{self.log_prefix} Sleep for {time_sleep} seconds before getting all opened PRs") time.sleep(time_sleep) for pull_request in self.repository.get_pulls(state="open"): self.pull_request = pull_request - self.app.logger.info(f"{self.log_prefix} check label pull request after merge") + LOGGER.info(f"{self.log_prefix} check label pull request after merge") self.label_pull_request_by_merge_state() def label_pull_request_by_merge_state(self, _sleep=30): if _sleep: - self.app.logger.info(f"{self.log_prefix} Sleep for 30 seconds before checking merge state") + LOGGER.info(f"{self.log_prefix} Sleep for 30 seconds before checking merge state") time.sleep(_sleep) merge_state = self.pull_request.mergeable_state - self.app.logger.info(f"{self.log_prefix} Mergeable state is {merge_state}") + LOGGER.info(f"{self.log_prefix} Mergeable state is {merge_state}") if merge_state == "unknown": return @@ -1353,7 +1347,7 @@ def check_if_can_be_merged(self): failure_output = "" try: - self.app.logger.info(f"{self.log_prefix} Check if {CAN_BE_MERGED_STR}.") + LOGGER.info(f"{self.log_prefix} Check if {CAN_BE_MERGED_STR}.") self.set_merge_check_queued() last_commit_check_runs = list(self.last_commit.get_check_runs()) check_runs_in_progress = [ @@ -1364,7 +1358,7 @@ def check_if_can_be_merged(self): and check_run.name in self.all_required_status_checks ] if check_runs_in_progress: - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} Some required check runs in progress {check_runs_in_progress}, " f"skipping check if {CAN_BE_MERGED_STR}." ) @@ -1401,7 +1395,7 @@ def check_if_can_be_merged(self): self._remove_label(label=CAN_BE_MERGED_STR) failure_output += f"Some check runs failed: {failed_check_runs}\n" - self.app.logger.info(f"{self.log_prefix} check if can be merged. PR labels are: {_labels}") + LOGGER.info(f"{self.log_prefix} check if can be merged. PR labels are: {_labels}") for _label in _labels: if CHANGED_REQUESTED_BY_LABEL_PREFIX.lower() in _label.lower(): @@ -1431,7 +1425,7 @@ def check_if_can_be_merged(self): self._add_label(label=CAN_BE_MERGED_STR) self.set_merge_check_success() if self.parent_committer in self.auto_verified_and_merged_users: - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} will be merged automatically. owner: {self.parent_committer} " f"is part of {self.auto_verified_and_merged_users}" ) @@ -1446,14 +1440,12 @@ def check_if_can_be_merged(self): failure_output += f"Missing lgtm/approved from approvers {self.approvers}\n" if failure_output: - self.app.logger.info(f"{self.log_prefix} cannot be merged: {failure_output}") + LOGGER.info(f"{self.log_prefix} cannot be merged: {failure_output}") output["text"] = failure_output self.set_merge_check_failure(output=output) except Exception as ex: - self.app.logger.error( - f"{self.log_prefix} Failed to check if can be merged, set check run to {FAILURE_STR} {ex}" - ) + LOGGER.error(f"{self.log_prefix} Failed to check if can be merged, set check run to {FAILURE_STR} {ex}") output["text"] = "Failed to check if can be merged, check logs" return self.set_merge_check_failure(output=output) @@ -1479,10 +1471,10 @@ def _container_repository_and_tag(self, is_merged=None, tag=None): tag = f"pr-{self.pull_request.number}" if tag: - self.app.logger.info(f"{self.log_prefix} container tag is: {tag}") + LOGGER.info(f"{self.log_prefix} container tag is: {tag}") return f"{self.container_repository}:{tag}" - self.app.logger.error(f"{self.log_prefix} container tag not found") + LOGGER.error(f"{self.log_prefix} container tag not found") @ignore_exceptions(logger=LOGGER) def _run_build_container( @@ -1497,7 +1489,7 @@ def _run_build_container( if set_check: if self.is_check_run_in_progress(check_run=BUILD_CONTAINER_STR) and not is_merged: - self.app.logger.info(f"{self.log_prefix} Check run is in progress, not running {BUILD_CONTAINER_STR}.") + LOGGER.info(f"{self.log_prefix} Check run is in progress, not running {BUILD_CONTAINER_STR}.") return False self.set_container_build_in_progress() @@ -1525,7 +1517,7 @@ def _run_build_container( "text": self.get_checkrun_text(err=err, out=out), } if rc: - self.app.logger.info(f"{self.log_prefix} Done building {_container_repository_and_tag}") + LOGGER.info(f"{self.log_prefix} Done building {_container_repository_and_tag}") if self.pull_request and set_check: return self.set_container_build_success(output=output) if push: @@ -1540,7 +1532,7 @@ def _run_build_container( """ self.send_slack_message(message=message, webhook_url=self.slack_webhook_url) - self.app.logger.info(f"{self.log_prefix} Done push {_container_repository_and_tag}") + LOGGER.info(f"{self.log_prefix} Done push {_container_repository_and_tag}") else: if push: err_msg = f"Failed to create and push {_container_repository_and_tag}" @@ -1561,12 +1553,10 @@ def _run_install_python_module(self): return False if self.is_check_run_in_progress(check_run=PYTHON_MODULE_INSTALL_STR): - self.app.logger.info( - f"{self.log_prefix} Check run is in progress, not running {PYTHON_MODULE_INSTALL_STR}." - ) + LOGGER.info(f"{self.log_prefix} Check run is in progress, not running {PYTHON_MODULE_INSTALL_STR}.") return False - self.app.logger.info(f"{self.log_prefix} Installing python module") + LOGGER.info(f"{self.log_prefix} Installing python module") f"{PYTHON_MODULE_INSTALL_STR}-{shortuuid.uuid()}" self.set_python_module_install_in_progress() rc, out, err = self._run_in_container(command="pip install .") @@ -1582,7 +1572,7 @@ def _run_install_python_module(self): def send_slack_message(self, message, webhook_url): slack_data = {"text": message} - self.app.logger.info(f"{self.log_prefix} Sending message to slack: {message}") + LOGGER.info(f"{self.log_prefix} Sending message to slack: {message}") response = requests.post( webhook_url, data=json.dumps(slack_data), @@ -1599,7 +1589,7 @@ def _process_verified(self): return if self.parent_committer in self.auto_verified_and_merged_users: - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} Committer {self.parent_committer} is part of {self.auto_verified_and_merged_users}" ", Setting verified label" ) @@ -1622,7 +1612,7 @@ def process_opened_or_synchronize_pull_request(self): self._process_verified() self.add_size_label() self._add_label(label=f"{BRANCH_LABEL_PREFIX}{self.pull_request_branch}") - self.app.logger.info(f"{self.log_prefix} Adding PR owner as assignee") + LOGGER.info(f"{self.log_prefix} Adding PR owner as assignee") try: self.pull_request.add_to_assignees() @@ -1642,8 +1632,8 @@ def process_opened_or_synchronize_pull_request(self): for result in as_completed(futures): if result.exception(): - self.app.logger.error(f"{self.log_prefix} {result.exception()}") - self.app.logger.info(f"{self.log_prefix} {result.result()}") + LOGGER.error(f"{self.log_prefix} {result.exception()}") + LOGGER.info(f"{self.log_prefix} {result.result()}") def is_check_run_in_progress(self, check_run): for run in self.last_commit.get_check_runs(): @@ -1663,15 +1653,15 @@ def set_check_run_status(self, check_run, status=None, conclusion=None, output=N kwargs["output"] = output msg = f"{self.log_prefix} Set {check_run} check to {status or conclusion}" - self.app.logger.info(msg) + LOGGER.info(msg) try: self.repository_by_github_app.create_check_run(**kwargs) if conclusion == SUCCESS_STR: - self.app.logger.success(msg) + LOGGER.success(msg) except Exception as ex: - self.app.logger.error(f"{self.log_prefix} Failed to set {check_run} check to {status or conclusion}, {ex}") + LOGGER.error(f"{self.log_prefix} Failed to set {check_run} check to {status or conclusion}, {ex}") kwargs["conclusion"] = FAILURE_STR self.repository_by_github_app.create_check_run(**kwargs) return f"Done setting check run status: {kwargs}" @@ -1708,7 +1698,7 @@ def _run_in_container(self, command, env=None, is_merged=False, checkout=None, t # Checkout the pull request else: if not self.pull_request: - self.app.logger.error(f"{self.log_prefix} [func:_run_in_container] No pull request found") + LOGGER.error(f"{self.log_prefix} [func:_run_in_container] No pull request found") return clone_base_cmd += f" && git checkout origin/pr/{self.pull_request.number}" @@ -1750,7 +1740,7 @@ def log_repository_features(self): jira-enabled-repository: {self.jira_enabled_repository} jira-user-mapping: {self.jira_user_mapping} """ - self.app.logger.info(f"{self.log_prefix} Repository features: {repository_features}") + LOGGER.info(f"{self.log_prefix} Repository features: {repository_features}") def get_story_key_with_jira_connection(self): _story_label = [_label for _label in self.pull_request.labels if _label.name.startswith(JIRA_STR)] @@ -1760,14 +1750,14 @@ def get_story_key_with_jira_connection(self): if _story_key := _story_label[0].name.split(":")[-1]: self.get_jira_conn() if not self.jira_conn: - self.app.logger.error(f"{self.log_prefix} Jira connection not found") + LOGGER.error(f"{self.log_prefix} Jira connection not found") return None return _story_key @ignore_exceptions(logger=LOGGER, return_on_error=[]) def get_branch_required_status_checks(self): if self.repository.private: - self.app.logger.info( + LOGGER.info( f"{self.log_prefix} Repository is private, skipping getting branch protection required status checks" ) return []