From 2cc1014b3f28199646398cd745f966490ae1a5a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Tue, 27 Jun 2023 12:16:04 +0200 Subject: [PATCH 1/5] feat: update to latest gateway --- .github/workflows/integration-gateway.yml | 114 ++--- .github/workflows/pytest-integration.yml | 2 +- .github/workflows/pytest.yml | 2 +- .github/workflows/python-publish.yml | 2 +- poetry.lock | 440 ++++++++++--------- pyproject.toml | 6 +- scw_serverless/cli.py | 35 +- scw_serverless/config/route.py | 11 +- scw_serverless/gateway/__init__.py | 2 + scw_serverless/gateway/gateway_api_client.py | 46 -- scw_serverless/gateway/gateway_manager.py | 20 +- scw_serverless/gateway/serverless_gateway.py | 50 +++ tests/integrations/conftest.py | 33 -- tests/integrations/gateway/test_gateway.py | 18 +- tests/integrations/utils.py | 19 +- tests/test_gateway/test_gateway_manager.py | 35 +- 16 files changed, 390 insertions(+), 445 deletions(-) delete mode 100644 scw_serverless/gateway/gateway_api_client.py create mode 100644 scw_serverless/gateway/serverless_gateway.py diff --git a/.github/workflows/integration-gateway.yml b/.github/workflows/integration-gateway.yml index b17c48f..5702c10 100644 --- a/.github/workflows/integration-gateway.yml +++ b/.github/workflows/integration-gateway.yml @@ -1,4 +1,5 @@ -name: API Gateway integration tests with Pytest +--- +name: API Gateway integration tests on: push: @@ -10,91 +11,62 @@ on: permissions: contents: read -env: - GATEWAY_CHECKOUT_DIR: "gateway" - S3_ENDPOINT: "https://s3.fr-par.scw.cloud" - S3_REGION: "fr-par" - -defaults: - run: - shell: bash - jobs: - test-deployed-gateway: + setup-gateway: runs-on: self-hosted - container: python:3.11-bullseye + container: python:3.11-bookworm steps: - uses: actions/checkout@v3 - uses: ./.github/actions/setup-poetry - - uses: actions/checkout@v3 - with: - repository: scaleway/serverless-gateway - path: ${{ env.GATEWAY_CHECKOUT_DIR }} - - - name: Install CLI and create config file - uses: scaleway/action-scw@v0.0.1 - with: - version: v2.14.0 - access-key: ${{ secrets.SCW_ACCESS_KEY }} - secret-key: ${{ secrets.SCW_SECRET_KEY }} - default-project-id: ${{ secrets.SCW_DEFAULT_PROJECT_ID }} - default-organization-id: ${{ secrets.SCW_DEFAULT_ORGANIZATION_ID }} - save-config: true - - - name: Install jq - run: apt-get update && apt-get install -y jq - - - name: Create Gateway namespace - working-directory: ${{ env.GATEWAY_CHECKOUT_DIR }} - run: | - make create-namespace - until [ $(make check-namespace -s) == ready ]; do sleep 10; done - - - name: Create Gateway container - working-directory: ${{ env.GATEWAY_CHECKOUT_DIR }} - # We need to truncate gateway.env as it will override our env vars - run: | - truncate -s 0 gateway.env - make create-container - make deploy-container - until [ $(make check-container -s) == ready ]; do sleep 10; done + - name: Deploy Serverless Gateway + run: poetry run scwgw deploy env: - SCW_ACCESS_KEY: ${{ secrets.SCW_ACCESS_KEY }} + SCW_DEFAULT_ORGANIZATION_ID: ${{ secrets.SCW_DEFAULT_ORGANIZATION_ID }} + SCW_DEFAULT_PROJECT_ID: ${{ secrets.SCW_DEFAULT_PROJECT_ID }} SCW_SECRET_KEY: ${{ secrets.SCW_SECRET_KEY }} - S3_BUCKET_NAME: ${{ secrets.GATEWAY_S3_BUCKET_NAME }} + SCW_ACCESS_KEY: ${{ secrets.SCW_ACCESS_KEY }} - - name: Install s3cmd - run: pip install s3cmd + run-tests: + needs: + - setup-gateway + runs-on: self-hosted + container: python:3.11-bookworm + steps: + - uses: actions/checkout@v3 - - name: Create S3 bucket - working-directory: ${{ env.GATEWAY_CHECKOUT_DIR }} - run: | - make set-up-s3-cli - make create-s3-bucket - env: - S3_BUCKET_NAME: ${{ secrets.GATEWAY_S3_BUCKET_NAME }} + - uses: ./.github/actions/setup-poetry - name: Run integration tests + working-directory: tests run: | - pushd $GATEWAY_CHECKOUT_DIR - export GATEWAY_HOST=$(make gateway-host -s) - popd - poetry run pytest tests/integrations/gateway -n $(nproc --all) + poetry run scwgw remote-config + poetry run pytest integrations/gateway -n $(nproc --all) env: - SCW_ACCESS_KEY: ${{ secrets.SCW_ACCESS_KEY }} + SCW_DEFAULT_ORGANIZATION_ID: ${{ secrets.SCW_DEFAULT_ORGANIZATION_ID }} + SCW_DEFAULT_PROJECT_ID: ${{ secrets.SCW_DEFAULT_PROJECT_ID }} SCW_SECRET_KEY: ${{ secrets.SCW_SECRET_KEY }} - GATEWAY_S3_BUCKET_NAME: ${{ secrets.GATEWAY_S3_BUCKET_NAME }} + SCW_ACCESS_KEY: ${{ secrets.SCW_ACCESS_KEY }} - - name: Delete S3 bucket - working-directory: ${{ env.GATEWAY_CHECKOUT_DIR }} - run: make delete-bucket - env: - S3_BUCKET_NAME: ${{ secrets.GATEWAY_S3_BUCKET_NAME }} - if: always() + teardown-gateway: + runs-on: self-hosted + container: python:3.11-bookworm + needs: + - run-tests - - name: Delete Gateway namespace and container - working-directory: ${{ env.GATEWAY_CHECKOUT_DIR }} - run: make delete-namespace - if: always() + # Tolerate errors if no resources found to clean up + continue-on-error: true + + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/setup-poetry + + - name: Delete Serverless Gateway + run: poetry run scwgw delete + env: + SCW_DEFAULT_ORGANIZATION_ID: ${{ secrets.SCW_DEFAULT_ORGANIZATION_ID }} + SCW_DEFAULT_PROJECT_ID: ${{ secrets.SCW_DEFAULT_PROJECT_ID }} + SCW_SECRET_KEY: ${{ secrets.SCW_SECRET_KEY }} + SCW_ACCESS_KEY: ${{ secrets.SCW_ACCESS_KEY }} diff --git a/.github/workflows/pytest-integration.yml b/.github/workflows/pytest-integration.yml index 51ef82f..ebd3f7f 100644 --- a/.github/workflows/pytest-integration.yml +++ b/.github/workflows/pytest-integration.yml @@ -13,7 +13,7 @@ permissions: jobs: test: runs-on: self-hosted - container: python:3.11-bullseye + container: python:3.11-bookworm steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 13bd5e3..6f6d407 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -14,7 +14,7 @@ permissions: jobs: test: runs-on: self-hosted - container: python:3.11-bullseye + container: python:3.11-bookworm steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 4f2234e..80b3d4e 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -11,7 +11,7 @@ permissions: jobs: deploy: runs-on: self-hosted - container: python:3.11-bullseye + container: python:3.11-bookworm steps: - uses: actions/checkout@v3 diff --git a/poetry.lock b/poetry.lock index 46c368a..602845f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -13,13 +14,14 @@ files = [ [[package]] name = "astroid" -version = "2.15.4" +version = "2.15.5" description = "An abstract syntax tree for Python with inference support." +category = "dev" optional = false python-versions = ">=3.7.2" files = [ - {file = "astroid-2.15.4-py3-none-any.whl", hash = "sha256:a1b8543ef9d36ea777194bc9b17f5f8678d2c56ee6a45b2c2f17eec96f242347"}, - {file = "astroid-2.15.4.tar.gz", hash = "sha256:c81e1c7fbac615037744d067a9bb5f9aeb655edf59b63ee8b59585475d6f80d8"}, + {file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"}, + {file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"}, ] [package.dependencies] @@ -34,6 +36,7 @@ wrapt = [ name = "babel" version = "2.12.1" description = "Internationalization utilities" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -45,6 +48,7 @@ files = [ name = "blinker" version = "1.6.2" description = "Fast, simple object-to-object and broadcast signaling" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -52,48 +56,11 @@ files = [ {file = "blinker-1.6.2.tar.gz", hash = "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213"}, ] -[[package]] -name = "boto3" -version = "1.26.155" -description = "The AWS SDK for Python" -optional = false -python-versions = ">= 3.7" -files = [ - {file = "boto3-1.26.155-py3-none-any.whl", hash = "sha256:dd15823e8c0554d98c18584d9a6a0342c67611c1114ef61495934c2e560f632c"}, - {file = "boto3-1.26.155.tar.gz", hash = "sha256:2d4095e2029ce5ceccb25591f13e55aa5b8ba17794de09963654bd9ced45158f"}, -] - -[package.dependencies] -botocore = ">=1.29.155,<1.30.0" -jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.6.0,<0.7.0" - -[package.extras] -crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] - -[[package]] -name = "botocore" -version = "1.29.155" -description = "Low-level, data-driven core of boto 3." -optional = false -python-versions = ">= 3.7" -files = [ - {file = "botocore-1.29.155-py3-none-any.whl", hash = "sha256:32d5da68212e10c060fd484f41df4f7048fc7731ccd16fd00e37b11b6e841142"}, - {file = "botocore-1.29.155.tar.gz", hash = "sha256:7fbb7ebba5f645c9750fe557b1ea789d40017a028cdaa2c22fcbf06d4a4d3c1d"}, -] - -[package.dependencies] -jmespath = ">=0.7.1,<2.0.0" -python-dateutil = ">=2.1,<3.0.0" -urllib3 = ">=1.25.4,<1.27" - -[package.extras] -crt = ["awscrt (==0.16.9)"] - [[package]] name = "certifi" version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -105,6 +72,7 @@ files = [ name = "cfgv" version = "3.3.1" description = "Validate configuration and produce human readable error messages." +category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -116,6 +84,7 @@ files = [ name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -200,6 +169,7 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -214,6 +184,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -225,6 +196,7 @@ files = [ name = "dill" version = "0.3.6" description = "serialize all of python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -239,6 +211,7 @@ graph = ["objgraph (>=1.7.2)"] name = "distlib" version = "0.3.6" description = "Distribution utilities" +category = "dev" optional = false python-versions = "*" files = [ @@ -250,6 +223,7 @@ files = [ name = "docutils" version = "0.18.1" description = "Docutils -- Python Documentation Utilities" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -261,6 +235,7 @@ files = [ name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -275,6 +250,7 @@ test = ["pytest (>=6)"] name = "execnet" version = "1.9.0" description = "execnet: rapid multi-Python deployment" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -287,23 +263,25 @@ testing = ["pre-commit"] [[package]] name = "filelock" -version = "3.12.0" +version = "3.12.2" description = "A platform independent file lock." +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, - {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "flask" version = "2.3.2" description = "A simple framework for building complex web applications." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -314,7 +292,6 @@ files = [ [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 = ">=2.3.3" @@ -327,6 +304,7 @@ dotenv = ["python-dotenv"] name = "identify" version = "2.5.24" description = "File identification library for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -341,6 +319,7 @@ license = ["ukkonen"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -352,6 +331,7 @@ files = [ name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -359,29 +339,11 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[[package]] -name = "importlib-metadata" -version = "6.6.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, - {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -393,6 +355,7 @@ files = [ name = "isort" version = "5.12.0" description = "A Python utility / library to sort Python imports." +category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -410,6 +373,7 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"] name = "itsdangerous" version = "2.1.2" description = "Safely pass data to untrusted environments and back." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -421,6 +385,7 @@ files = [ name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -434,21 +399,11 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[[package]] -name = "jmespath" -version = "1.0.1" -description = "JSON Matching Expressions" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, - {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, -] - [[package]] name = "lazy-object-proxy" version = "1.9.0" description = "A fast and thorough lazy object proxy." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -490,10 +445,30 @@ files = [ {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, ] +[[package]] +name = "loguru" +version = "0.6.0" +description = "Python logging made (stupidly) simple" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"}, + {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (>=4.1.1)", "black (>=19.10b0)", "colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)"] + [[package]] name = "markdown-it-py" version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -516,67 +491,69 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.2" +version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, - {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] [[package]] name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -588,6 +565,7 @@ files = [ name = "mdit-py-plugins" version = "0.3.5" description = "Collection of plugins for markdown-it-py" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -607,6 +585,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -618,6 +597,7 @@ files = [ name = "myst-parser" version = "1.0.0" description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -642,13 +622,14 @@ testing-docutils = ["pygments", "pytest (>=7,<8)", "pytest-param-files (>=0.3.4, [[package]] name = "nodeenv" -version = "1.7.0" +version = "1.8.0" description = "Node.js virtual environment builder" +category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] @@ -658,6 +639,7 @@ setuptools = "*" name = "packaging" version = "23.1" description = "Core utilities for Python packages" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -667,28 +649,30 @@ files = [ [[package]] name = "platformdirs" -version = "3.5.0" +version = "3.8.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.5.0-py3-none-any.whl", hash = "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4"}, - {file = "platformdirs-3.5.0.tar.gz", hash = "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335"}, + {file = "platformdirs-3.8.0-py3-none-any.whl", hash = "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e"}, + {file = "platformdirs-3.8.0.tar.gz", hash = "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" -version = "1.0.0" +version = "1.2.0" description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, ] [package.extras] @@ -699,6 +683,7 @@ testing = ["pytest", "pytest-benchmark"] name = "pre-commit" version = "3.3.3" description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -717,6 +702,7 @@ virtualenv = ">=20.10.0" name = "pygments" version = "2.15.1" description = "Pygments is a syntax highlighting package written in Python." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -731,6 +717,7 @@ plugins = ["importlib-metadata"] name = "pylint" version = "2.17.4" description = "python code static checker" +category = "dev" optional = false python-versions = ">=3.7.2" files = [ @@ -750,7 +737,6 @@ mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [package.extras] spelling = ["pyenchant (>=3.2,<4.0)"] @@ -760,6 +746,7 @@ testutils = ["gitpython (>3)"] name = "pylint-per-file-ignores" version = "1.2.1" description = "A pylint plugin to ignore error codes per file." +category = "dev" optional = false python-versions = ">=3.8.1,<4.0.0" files = [ @@ -772,13 +759,14 @@ tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} [[package]] name = "pytest" -version = "7.3.1" +version = "7.4.0" description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, - {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, ] [package.dependencies] @@ -790,12 +778,13 @@ pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-xdist" version = "3.3.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -816,6 +805,7 @@ testing = ["filelock"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -830,6 +820,7 @@ six = ">=1.5" name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -879,6 +870,7 @@ files = [ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -900,6 +892,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "responses" version = "0.23.1" description = "A utility library for mocking out the `requests` Python library." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -916,32 +909,16 @@ urllib3 = ">=1.25.10" [package.extras] tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli", "tomli-w", "types-requests"] -[[package]] -name = "s3transfer" -version = "0.6.1" -description = "An Amazon S3 Transfer Manager" -optional = false -python-versions = ">= 3.7" -files = [ - {file = "s3transfer-0.6.1-py3-none-any.whl", hash = "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346"}, - {file = "s3transfer-0.6.1.tar.gz", hash = "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9"}, -] - -[package.dependencies] -botocore = ">=1.12.36,<2.0a.0" - -[package.extras] -crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"] - [[package]] name = "scaleway" -version = "0.14.1" +version = "0.12.0" description = "Scaleway SDK for Python" +category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "scaleway-0.14.1-py3-none-any.whl", hash = "sha256:7db7f5f3c586095192045cc5410140138eae95e7326039be59326007a3bf6c31"}, - {file = "scaleway-0.14.1.tar.gz", hash = "sha256:fd69b705931173d0ec78dfa0dee45aa9c351d92dfc70a3bcc53a7e0cd4fb7928"}, + {file = "scaleway-0.12.0-py3-none-any.whl", hash = "sha256:7fa1a67173424f172c8fc6e555dd8b3aee2807d8aaf7efceb164877050022886"}, + {file = "scaleway-0.12.0.tar.gz", hash = "sha256:76ec8d95d1972727c25070f5f2aaa1ca46aa411090d007b5b8aa39f449f00544"}, ] [package.dependencies] @@ -949,13 +926,14 @@ scaleway-core = ">=0,<1" [[package]] name = "scaleway-core" -version = "0.12.0" +version = "0.14.1" description = "Scaleway SDK for Python" +category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "scaleway_core-0.12.0-py3-none-any.whl", hash = "sha256:7994881ff9ebee4958e977624efc7048dfb22936e87e9b5c358552716f7ea355"}, - {file = "scaleway_core-0.12.0.tar.gz", hash = "sha256:c7ae4b87efd4650ef7c723aee6ec080b48a9db073b27ce6d47208892c0a3d3bc"}, + {file = "scaleway_core-0.14.1-py3-none-any.whl", hash = "sha256:f24013aa90455fcefbe80fd180271fa6576e80ce122507365b1e3ed62d7d8a62"}, + {file = "scaleway_core-0.14.1.tar.gz", hash = "sha256:d6fea67a48826cd6e994d93731d9d5a1355a833789a655901607453c97d74a89"}, ] [package.dependencies] @@ -967,6 +945,7 @@ requests = ">=2.28.1,<3.0.0" name = "scaleway-functions-python" version = "0.2.0" description = "Utilities for testing your Python handlers for Scaleway Serverless Functions." +category = "main" optional = false python-versions = ">=3.8.1,<3.12" files = [ @@ -978,26 +957,47 @@ files = [ flask = ">=2.2.2,<3.0.0" typing-extensions = {version = ">=4.4.0,<5.0.0", markers = "python_version < \"3.11\""} +[[package]] +name = "scw-gateway" +version = "0.3.1" +description = "CLI to deploy and manage a self-hosted Kong gateway on Scaleway Serverless Ecosystem" +category = "dev" +optional = false +python-versions = ">=3.10,<4.0" +files = [ + {file = "scw_gateway-0.3.1-py3-none-any.whl", hash = "sha256:7c5c58f6acfaec6ab20d31e369b6eb3a6209f9a09a62e5c15e4f10e6a03a7631"}, + {file = "scw_gateway-0.3.1.tar.gz", hash = "sha256:0892390fe29ba0a4d2bafd1fbd0efc2c132a078136d90d2e7e9b2ba7bac951ee"}, +] + +[package.dependencies] +click = ">=8.1.3,<9.0.0" +loguru = "0.6.0" +pyyaml = ">=6.0,<7.0" +requests = ">=2.28.2,<3.0.0" +scaleway = ">=0.12.0,<0.13.0" + [[package]] name = "setuptools" -version = "67.7.2" +version = "68.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1009,6 +1009,7 @@ files = [ name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" optional = false python-versions = "*" files = [ @@ -1020,6 +1021,7 @@ files = [ name = "sphinx" version = "6.2.1" description = "Python documentation generator" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1033,7 +1035,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.20" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.13" @@ -1055,6 +1056,7 @@ test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] name = "sphinx-rtd-theme" version = "1.2.2" description = "Read the Docs theme for Sphinx" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -1074,6 +1076,7 @@ dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] name = "sphinxcontrib-applehelp" version = "1.0.4" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1089,6 +1092,7 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1104,6 +1108,7 @@ test = ["pytest"] name = "sphinxcontrib-htmlhelp" version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1119,6 +1124,7 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jquery" version = "4.1" description = "Extension to include jQuery on newer Sphinx releases" +category = "dev" optional = false python-versions = ">=2.7" files = [ @@ -1133,6 +1139,7 @@ Sphinx = ">=1.8" name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1147,6 +1154,7 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1162,6 +1170,7 @@ test = ["pytest"] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1177,6 +1186,7 @@ test = ["pytest"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1188,6 +1198,7 @@ files = [ name = "tomlkit" version = "0.11.8" description = "Style preserving TOML library" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1197,19 +1208,21 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.9" +version = "6.0.12.10" description = "Typing stubs for PyYAML" +category = "dev" optional = false python-versions = "*" files = [ - {file = "types-PyYAML-6.0.12.9.tar.gz", hash = "sha256:c51b1bd6d99ddf0aa2884a7a328810ebf70a4262c292195d3f4f9a0005f9eeb6"}, - {file = "types_PyYAML-6.0.12.9-py3-none-any.whl", hash = "sha256:5aed5aa66bd2d2e158f75dda22b059570ede988559f030cf294871d3b647e3e8"}, + {file = "types-PyYAML-6.0.12.10.tar.gz", hash = "sha256:ebab3d0700b946553724ae6ca636ea932c1b0868701d4af121630e78d695fc97"}, + {file = "types_PyYAML-6.0.12.10-py3-none-any.whl", hash = "sha256:662fa444963eff9b68120d70cda1af5a5f2aa57900003c2006d7626450eaae5f"}, ] [[package]] name = "typing-extensions" version = "4.6.3" description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1219,49 +1232,53 @@ files = [ [[package]] name = "urllib3" -version = "1.26.15" +version = "2.0.3" description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7" files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, + {file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"}, + {file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.23.0" +version = "20.23.1" description = "Virtual Python Environment builder" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, - {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, + {file = "virtualenv-20.23.1-py3-none-any.whl", hash = "sha256:34da10f14fea9be20e0fd7f04aba9732f84e593dac291b757ce42e3368a39419"}, + {file = "virtualenv-20.23.1.tar.gz", hash = "sha256:8ff19a38c1021c742148edc4f81cb43d7f8c6816d2ede2ab72af5b84c749ade1"}, ] [package.dependencies] distlib = ">=0.3.6,<1" -filelock = ">=3.11,<4" -platformdirs = ">=3.2,<4" +filelock = ">=3.12,<4" +platformdirs = ">=3.5.1,<4" [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "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.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"] [[package]] name = "werkzeug" -version = "2.3.4" +version = "2.3.6" description = "The comprehensive WSGI web application library." +category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "Werkzeug-2.3.4-py3-none-any.whl", hash = "sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f"}, - {file = "Werkzeug-2.3.4.tar.gz", hash = "sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76"}, + {file = "Werkzeug-2.3.6-py3-none-any.whl", hash = "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890"}, + {file = "Werkzeug-2.3.6.tar.gz", hash = "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330"}, ] [package.dependencies] @@ -1270,10 +1287,26 @@ MarkupSafe = ">=2.1.1" [package.extras] watchdog = ["watchdog (>=2.3)"] +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, + {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, +] + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + [[package]] name = "wrapt" version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -1354,22 +1387,7 @@ files = [ {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, ] -[[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - [metadata] lock-version = "2.0" -python-versions = ">=3.9,<3.12" -content-hash = "98daa3abeeeb70cedd2b787c90c743ff39a12bcce27662a1663eab6758830ad6" +python-versions = ">=3.10,<3.12" +content-hash = "18bd890a9d52e1dcd3f737f768222f201ee25d3a6bae7e6f96f49a17e4fc0bbc" diff --git a/pyproject.toml b/pyproject.toml index f9326c5..a8a472b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ classifiers = [ "Topic :: Internet", "License :: OSI Approved :: MIT License", "Programming Language :: Python", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", ] @@ -33,7 +32,8 @@ include = ["CHANGELOG.md"] scw-serverless = "scw_serverless.cli:cli" [tool.poetry.dependencies] -python = ">=3.9,<3.12" +# TODO: investigate this breaking change (can we lower the gateway Python version ?) +python = ">=3.10,<3.12" click = "^8.1.3" scaleway = ">=0.7,<0.15" scaleway-functions-python = "^0.2.0" @@ -47,7 +47,7 @@ pytest-xdist = "^3.1.0" pylint = "^2.15.10" pylint-per-file-ignores = "^1.1.0" responses = ">=0.22,<0.24" -boto3 = "^1.26.130" +scw-gateway = "^0.3.0" [tool.poetry.group.doc] optional = true diff --git a/scw_serverless/cli.py b/scw_serverless/cli.py index 9e5846c..48854c9 100644 --- a/scw_serverless/cli.py +++ b/scw_serverless/cli.py @@ -8,7 +8,7 @@ import scw_serverless from scw_serverless import app, deployment, loader, local_app, logger from scw_serverless.dependencies_manager import DependenciesManager -from scw_serverless.gateway.gateway_manager import GatewayManager +from scw_serverless.gateway import GatewayManager, ServerlessGateway CLICK_ARG_FILE = click.argument( "file", @@ -43,16 +43,6 @@ def cli(verbose: bool, log_level: str) -> None: @cli.command() @CLICK_ARG_FILE -@click.option( - "--gateway-url", - default=None, - help="URL of a deployed API Gateway.", -) -@click.option( - "--gateway-api-key", - default=None, - help="API key used to manage the routes of the API Gateway.", -) @click.option( "--runtime", default=None, @@ -84,8 +74,6 @@ def cli(verbose: bool, log_level: str) -> None: # pylint: disable=too-many-arguments def deploy( file: Path, - gateway_url: Optional[str], - gateway_api_key: Optional[str], runtime: Optional[str], single_source: bool, profile: Optional[str] = None, @@ -105,18 +93,10 @@ def deploy( # Check if the application requires a Gateway needs_gateway = any(function.gateway_route for function in app_instance.functions) - if needs_gateway and not gateway_url: - logging.debug("Prompting for Serverless Gateway URL...") - gateway_url = click.prompt( - "Please enter the URL of your Serverless Gateway", type=str - ) - if needs_gateway and not gateway_api_key: - logging.debug("Prompting for Serverless Gateway API key...") - gateway_url = click.prompt( - "Please enter the API key to your Serverless Gateway", - hide_input=True, - type=str, - ) + gateway = None + if needs_gateway: + logging.debug("Checking for Gateway CLI") + gateway = ServerlessGateway() client = deployment.get_scw_client(profile, secret_key, project_id, region) @@ -139,10 +119,11 @@ def deploy( deployment.log_scaleway_exception(e) if needs_gateway: + assert gateway + manager = GatewayManager( app_instance=app_instance, - gateway_url=cast(str, gateway_url), - gateway_api_key=cast(str, gateway_api_key), + gateway=gateway, sdk_client=client, ) manager.update_routes() diff --git a/scw_serverless/config/route.py b/scw_serverless/config/route.py index 37992b2..9134b3d 100644 --- a/scw_serverless/config/route.py +++ b/scw_serverless/config/route.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from enum import Enum -from typing import Any, Optional +from typing import Optional class HTTPMethod(Enum): @@ -33,12 +33,3 @@ def validate(self) -> None: for method in self.http_methods or []: if method not in HTTPMethod: raise RuntimeError(f"Route contains invalid method {method.value}") - - def asdict(self) -> dict[str, Any]: - """Return a dict representation of a route.""" - serialized: dict[str, Any] = {"relative_url": self.relative_url} - if self.http_methods: - serialized["http_methods"] = [method.value for method in self.http_methods] - if self.target: - serialized["target"] = self.target - return serialized diff --git a/scw_serverless/gateway/__init__.py b/scw_serverless/gateway/__init__.py index e69de29..b327a22 100644 --- a/scw_serverless/gateway/__init__.py +++ b/scw_serverless/gateway/__init__.py @@ -0,0 +1,2 @@ +from .gateway_manager import GatewayManager as GatewayManager +from .serverless_gateway import ServerlessGateway as ServerlessGateway diff --git a/scw_serverless/gateway/gateway_api_client.py b/scw_serverless/gateway/gateway_api_client.py deleted file mode 100644 index 6f8dc5c..0000000 --- a/scw_serverless/gateway/gateway_api_client.py +++ /dev/null @@ -1,46 +0,0 @@ -import requests - -from scw_serverless.config.route import GatewayRoute - - -class GatewayAPIClient: - """A client for the API to manage routes on the Gateway.""" - - def __init__(self, gateway_url: str, gateway_api_key: str): - self.url = gateway_url + "/scw" - - self.session = requests.Session() - self.session.headers["X-Auth-Token"] = gateway_api_key - - def get_all(self) -> list[GatewayRoute]: - """Get all previously defined routes.""" - - resp = self.session.get(self.url) - resp.raise_for_status() - - endpoints = resp.json()["endpoints"] - routes = [] - for endpoint in endpoints: - routes.append( - GatewayRoute( - relative_url=endpoint["relative_url"], - http_methods=endpoint.get("http_methods"), - target=endpoint["target"], - ) - ) - - return routes - - def create_route(self, route: GatewayRoute) -> None: - """Create a route on the API Gateway.""" - - route.validate() - - resp = self.session.post(self.url, json=route.asdict()) - resp.raise_for_status() - - def delete_route(self, route: GatewayRoute) -> None: - """Delete a route on the API Gateway.""" - - resp = self.session.delete(self.url, json=route.asdict()) - resp.raise_for_status() diff --git a/scw_serverless/gateway/gateway_manager.py b/scw_serverless/gateway/gateway_manager.py index 94bd83e..0bad93b 100644 --- a/scw_serverless/gateway/gateway_manager.py +++ b/scw_serverless/gateway/gateway_manager.py @@ -1,8 +1,17 @@ +from typing import Protocol + import scaleway.function.v1beta1 as sdk from scaleway import Client from scw_serverless.app import Serverless -from scw_serverless.gateway.gateway_api_client import GatewayAPIClient +from scw_serverless.config.route import GatewayRoute + + +class Gateway(Protocol): + """Generic Gateway Implementation.""" + + def add_route(self, route: GatewayRoute) -> None: + """Add a route to the Gateway.""" class GatewayManager: @@ -11,15 +20,12 @@ class GatewayManager: def __init__( self, app_instance: Serverless, - gateway_url: str, - gateway_api_key: str, + gateway: Gateway, sdk_client: Client, ): self.app_instance = app_instance self.api = sdk.FunctionV1Beta1API(sdk_client) - self.gateway_client = GatewayAPIClient( - gateway_url=gateway_url, gateway_api_key=gateway_api_key - ) + self.gateway = gateway def _list_created_functions(self) -> dict[str, sdk.Function]: """Get the list of created functions.""" @@ -61,4 +67,4 @@ def update_routes(self) -> None: function.gateway_route.target = target # type: ignore for function in routed_functions: - self.gateway_client.create_route(function.gateway_route) # type: ignore + self.gateway.add_route(function.gateway_route) # type: ignore diff --git a/scw_serverless/gateway/serverless_gateway.py b/scw_serverless/gateway/serverless_gateway.py new file mode 100644 index 0000000..d5dde7c --- /dev/null +++ b/scw_serverless/gateway/serverless_gateway.py @@ -0,0 +1,50 @@ +import shutil +import subprocess + +import click + +from scw_serverless.config.route import GatewayRoute + +GATEWAY_CLI = "scwgw" + + +class ServerlessGateway: + """Manage routes on a Kong Gateway with scwgw.""" + + cli: str + + def __init__(self) -> None: + cli = shutil.which(GATEWAY_CLI) + + if not cli: + click.secho( + "scwgw was not found in $PATH, " + + "you can install scwgw by running: pip install scwgw", + fg="red", + ) + raise RuntimeError("scwgw is not installed") + + self.cli = cli + + def _invoke_cli(self, args: list[str]) -> subprocess.CompletedProcess: + cmd = subprocess.run( + args=[self.cli] + args, + check=True, + stdout=subprocess.PIPE, + universal_newlines=True, + ) + return cmd + + def add_route(self, route: GatewayRoute) -> None: + """Add a route to the gateway with the CLI.""" + + if not route.target: + raise RuntimeError(f"route {route.relative_url} is missing upstream target") + + cli_args = ["add-route", route.relative_url, route.target] + for method in route.http_methods or []: + cli_args += ["--http-methods", method.value] + + cmd = self._invoke_cli(cli_args) + for char in cmd.stdout: + print(char, end="") diff --git a/tests/integrations/conftest.py b/tests/integrations/conftest.py index f3c440d..ff5df04 100644 --- a/tests/integrations/conftest.py +++ b/tests/integrations/conftest.py @@ -4,17 +4,13 @@ import time import typing as t -import boto3 import pytest -import requests from click.testing import CliRunner from scaleway import Client from scaleway.account.v2 import AccountV2API from scaleway.function.v1beta1 import FunctionV1Beta1API from scaleway_core.api import ScalewayException -from tests import constants - from .utils import create_client COMMIT_SHA = os.getenv("GITHUB_SHA", "local") @@ -48,35 +44,6 @@ def scaleway_project() -> t.Iterator[str]: _delete_scaleway_project(client, project_id) -@pytest.fixture() -def gateway_auth_key() -> str: - assert constants.GATEWAY_HOST, "Gateway needs to be configured." - - client = create_client() - - response = requests.post( - "https://" + constants.GATEWAY_HOST + "/token", - timeout=constants.COLD_START_TIMEOUT, - ) - response.raise_for_status() - - s3 = boto3.resource( - "s3", - region_name=constants.GATEWAY_S3_BUCKET_NAME, - endpoint_url=constants.GATEWAY_S3_BUCKET_ENDPOINT, - aws_access_key_id=client.access_key, - aws_secret_access_key=client.secret_key, - ) - - objects = sorted( - s3.Bucket(constants.GATEWAY_S3_BUCKET_NAME).objects.all(), # type: ignore - key=lambda obj: obj.last_modified, - reverse=True, - ) - key = objects[0].key - return key - - def _create_scaleway_project(client: Client) -> str: name = f"apifw-{COMMIT_SHA[:7]}-{random.randint(0, 9999)}" # nosec # unsafe rng logging.info("Creating project %s for integration tests", name) diff --git a/tests/integrations/gateway/test_gateway.py b/tests/integrations/gateway/test_gateway.py index c96293c..3b59671 100644 --- a/tests/integrations/gateway/test_gateway.py +++ b/tests/integrations/gateway/test_gateway.py @@ -6,29 +6,25 @@ from tests.integrations import utils -def test_integration_gateway(cli_runner: CliRunner, gateway_auth_key: str): - gateway_url = f"https://{constants.GATEWAY_HOST}" +def test_integration_gateway(cli_runner: CliRunner): messages = routed_functions.MESSAGES + gateway_domain = utils.get_gateway_endpoint() + gateway_url = "https://" + gateway_domain + res = utils.run_deploy_command( cli_runner, app=routed_functions, - args=[ - "--gateway-url", - gateway_url, - "--gateway-api-key", - gateway_auth_key, - ], ) assert res.exit_code == 0, res.output # Check general routing configuration - resp = requests.get( - url=gateway_url + "/health", timeout=constants.COLD_START_TIMEOUT + # Wait as the gateway is not immediately available + resp = utils.wait_for_body_text( + domain_name=gateway_domain + "/health", body=messages["/health"] ) assert resp.status_code == 200 - assert resp.text == messages["/health"] # Test with common prefix with configured routes resp = requests.get( diff --git a/tests/integrations/utils.py b/tests/integrations/utils.py index 329be20..2d7d65e 100644 --- a/tests/integrations/utils.py +++ b/tests/integrations/utils.py @@ -1,5 +1,7 @@ import os +import re import shutil +import subprocess import time from pathlib import Path from types import ModuleType @@ -59,6 +61,19 @@ def get_deployed_functions_by_name(client: Client, app_instance: Serverless): return {function.name: function for function in deployed_functions} +def get_gateway_endpoint() -> str: + cmd = subprocess.run( + args=["scwgw", "get-endpoint"], + check=True, + capture_output=True, + ) + output = cmd.stdout.decode().strip() + match = re.search(r"(.*.functions.fnc.fr-par.scw.cloud)", output) + assert match, f"Could not find gateway endpoint in output: {output}" + + return match.group(1) + + def trigger_function(domain_name: str, max_retries: int = 10) -> requests.Response: url = f"https://{domain_name}" session = requests.Session() @@ -66,7 +81,7 @@ def trigger_function(domain_name: str, max_retries: int = 10) -> requests.Respon total=max_retries, backoff_factor=2, status=max_retries, # Status max retries - status_forcelist=[500, 404], + status_forcelist=[500, 503, 404], raise_on_status=False, # Raise for status called after ) session.mount("https://", HTTPAdapter(max_retries=retries)) @@ -76,7 +91,7 @@ def trigger_function(domain_name: str, max_retries: int = 10) -> requests.Respon def wait_for_body_text( - domain_name: str, body: str, max_retries: int = 20 + domain_name: str, body: str, max_retries: int = 30 ) -> requests.Response: last_body = None for _ in range(max_retries): diff --git a/tests/test_gateway/test_gateway_manager.py b/tests/test_gateway/test_gateway_manager.py index 81b813c..5e362ed 100644 --- a/tests/test_gateway/test_gateway_manager.py +++ b/tests/test_gateway/test_gateway_manager.py @@ -1,6 +1,8 @@ +from unittest.mock import MagicMock + import pytest import responses -from responses.matchers import header_matcher, json_params_matcher, query_param_matcher +from responses.matchers import query_param_matcher from scaleway import Client from scw_serverless.app import Serverless @@ -12,10 +14,6 @@ HELLO_WORLD_MOCK_DOMAIN = ( "helloworldfunctionnawns8i8vo-hello-world.functions.fnc.fr-par.scw.cloud" ) -MOCK_GATEWAY_URL = "https://my-gateway-domain.com" -MOCK_GATEWAY_API_KEY = "7tfxBRB^vJbBcR5s#*RE" -MOCK_UUID = "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" -PROJECT_ID = "projecti-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" # pylint: disable=redefined-outer-name # fixture @@ -34,18 +32,17 @@ def app_gateway_manager() -> GatewayManager: secret_key="498cce73-2a07-4e8c-b8ef-8f988e3c6929", # nosec # false positive default_region=constants.DEFAULT_REGION, ) - return GatewayManager(app, MOCK_GATEWAY_URL, MOCK_GATEWAY_API_KEY, client) + return GatewayManager(app_instance=app, gateway=MagicMock(), sdk_client=client) def test_gateway_manager_update_routes( app_gateway_manager: GatewayManager, mocked_responses: responses.RequestsMock ): + gateway_route = GatewayRoute(relative_url="/hello", http_methods=[HTTPMethod.GET]) function = Function( name="test-function", handler_path="handler", - gateway_route=GatewayRoute( - relative_url="/hello", http_methods=[HTTPMethod.GET] - ), + gateway_route=gateway_route, ) app_gateway_manager.app_instance.functions = [function] @@ -85,16 +82,12 @@ def test_gateway_manager_update_routes( json={"functions": []}, ) - # We should attempt to create the route - mocked_responses.post( - MOCK_GATEWAY_URL + "/scw", # type: ignore - match=[ - header_matcher({"X-Auth-Token": MOCK_GATEWAY_API_KEY}), - json_params_matcher( - params=function.gateway_route.asdict() # type: ignore - | {"target": "https://" + HELLO_WORLD_MOCK_DOMAIN} - ), - ], - ) - app_gateway_manager.update_routes() + + # Update the route with the domain name + gateway_route.target = HELLO_WORLD_MOCK_DOMAIN + + gateway_mock = app_gateway_manager.gateway + gateway_mock.add_route.assert_called_once_with( + gateway_route, + ) From c36cf45ef800bde9f0c444d0155ade021241f6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Tue, 27 Jun 2023 14:15:11 +0200 Subject: [PATCH 2/5] fix(test): retry on 503 status --- tests/integrations/gateway/test_gateway.py | 24 +++++++++++----------- tests/integrations/utils.py | 9 ++++++-- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/tests/integrations/gateway/test_gateway.py b/tests/integrations/gateway/test_gateway.py index 3b59671..b62af1e 100644 --- a/tests/integrations/gateway/test_gateway.py +++ b/tests/integrations/gateway/test_gateway.py @@ -1,4 +1,3 @@ -import requests from click.testing import CliRunner from tests import constants @@ -9,37 +8,36 @@ def test_integration_gateway(cli_runner: CliRunner): messages = routed_functions.MESSAGES - gateway_domain = utils.get_gateway_endpoint() - gateway_url = "https://" + gateway_domain + gateway_url = "https://" + utils.get_gateway_endpoint() res = utils.run_deploy_command( cli_runner, app=routed_functions, ) - assert res.exit_code == 0, res.output - # Check general routing configuration # Wait as the gateway is not immediately available - resp = utils.wait_for_body_text( - domain_name=gateway_domain + "/health", body=messages["/health"] - ) + session = utils.get_retry_session() + + # Check general routing configuration + resp = session.get(gateway_url + "/health", timeout=constants.COLD_START_TIMEOUT) assert resp.status_code == 200 + assert resp.text == messages["/health"] # Test with common prefix with configured routes - resp = requests.get( + resp = session.get( url=gateway_url + "/messages", timeout=constants.COLD_START_TIMEOUT ) assert resp.status_code == 200 assert resp.text == messages["/messages"] # Check a route with a method that is not configured - resp = requests.post( + resp = session.post( url=gateway_url + "/messages", timeout=constants.COLD_START_TIMEOUT ) assert resp.status_code == 404 - resp = requests.post( + resp = session.post( url=gateway_url + "/messages/new", timeout=constants.COLD_START_TIMEOUT, data="welcome", @@ -47,7 +45,9 @@ def test_integration_gateway(cli_runner: CliRunner): assert resp.status_code == 200 assert "welcome" in resp.text - resp = requests.put( + # Check that the routes are not greedy + # eg: /messages/new should not match /messages + resp = session.put( url=gateway_url + "/messages/welcome", timeout=constants.COLD_START_TIMEOUT, ) diff --git a/tests/integrations/utils.py b/tests/integrations/utils.py index 2d7d65e..4d1104c 100644 --- a/tests/integrations/utils.py +++ b/tests/integrations/utils.py @@ -74,8 +74,7 @@ def get_gateway_endpoint() -> str: return match.group(1) -def trigger_function(domain_name: str, max_retries: int = 10) -> requests.Response: - url = f"https://{domain_name}" +def get_retry_session(max_retries: int = 10) -> requests.Session: session = requests.Session() retries = Retry( total=max_retries, @@ -85,6 +84,12 @@ def trigger_function(domain_name: str, max_retries: int = 10) -> requests.Respon raise_on_status=False, # Raise for status called after ) session.mount("https://", HTTPAdapter(max_retries=retries)) + return session + + +def trigger_function(domain_name: str, max_retries: int = 10) -> requests.Response: + url = f"https://{domain_name}" + session = get_retry_session(max_retries=max_retries) req = session.get(url, timeout=constants.COLD_START_TIMEOUT) req.raise_for_status() return req From 7dc1c299864afc404e1039857b68466be6f6787b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Tue, 27 Jun 2023 14:30:04 +0200 Subject: [PATCH 3/5] fix(ci): disable interactive mode in teardown --- .github/workflows/integration-gateway.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-gateway.yml b/.github/workflows/integration-gateway.yml index 5702c10..7b476e2 100644 --- a/.github/workflows/integration-gateway.yml +++ b/.github/workflows/integration-gateway.yml @@ -64,7 +64,7 @@ jobs: - uses: ./.github/actions/setup-poetry - name: Delete Serverless Gateway - run: poetry run scwgw delete + run: poetry run scwgw delete -y env: SCW_DEFAULT_ORGANIZATION_ID: ${{ secrets.SCW_DEFAULT_ORGANIZATION_ID }} SCW_DEFAULT_PROJECT_ID: ${{ secrets.SCW_DEFAULT_PROJECT_ID }} From afa2dfc3cd8b6c420e1ab27f2c1894675f9854d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Tue, 27 Jun 2023 14:49:19 +0200 Subject: [PATCH 4/5] refactor: remove old gateway constants --- tests/constants.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/constants.py b/tests/constants.py index 2854926..97e44ab 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -9,13 +9,6 @@ COLD_START_TIMEOUT = 20 -GATEWAY_HOST = os.getenv("GATEWAY_HOST") -GATEWAY_S3_REGION = os.getenv("S3_REGION", str(DEFAULT_REGION)) -GATEWAY_S3_BUCKET_ENDPOINT = os.getenv( - "S3_ENDPOINT", f"https://s3.{DEFAULT_REGION}.scw.cloud" -) -GATEWAY_S3_BUCKET_NAME = os.getenv("GATEWAY_S3_BUCKET_NAME") - TESTS_DIR = os.path.realpath(os.path.dirname(__file__)) PROJECT_DIR = Path(TESTS_DIR).parent From 1aa6f2b1f0d64909d4144a27d8758bcfba82a545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Tue, 27 Jun 2023 15:19:19 +0200 Subject: [PATCH 5/5] fix(tests): pass env when calling get_gateway_endpoint --- tests/integrations/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integrations/utils.py b/tests/integrations/utils.py index 4d1104c..58272e1 100644 --- a/tests/integrations/utils.py +++ b/tests/integrations/utils.py @@ -66,6 +66,7 @@ def get_gateway_endpoint() -> str: args=["scwgw", "get-endpoint"], check=True, capture_output=True, + env=os.environ, ) output = cmd.stdout.decode().strip() match = re.search(r"(.*.functions.fnc.fr-par.scw.cloud)", output)