diff --git a/.github/workflows/build-artifacts.yml b/.github/workflows/build-artifacts.yml new file mode 100644 index 0000000..f32467b --- /dev/null +++ b/.github/workflows/build-artifacts.yml @@ -0,0 +1,51 @@ +# Build envsub tarballs for supported python. + +name: "Build artifact" + +on: + workflow_call: + inputs: + release-version: + required: true + type: string + dry-run: + required: true + type: boolean + python-version: + required: true + type: string + pull_request: + paths: + # When we change pyproject.toml, we want to ensure that the maturin builds still work. + - pyproject.toml + # And when we change this workflow itself... + - .github/workflows/build-artifacts.yml + +concurrency: + group: sdist-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + sdist: + name: Build artifact for ${{ inputs.release-version }} ${{ inputs.dry-run && '(dry-run)' || '' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + + - name: Install uv + uses: astral-sh/setup-uv@v3 + + - name: Install the project + run: uv sync + + - name: Build tarball + run: uv build + + - name: "Upload sdist" + uses: actions/upload-artifact@v4 + with: + name: pypi_files + path: dist/* diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 0000000..8032b77 --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,35 @@ +# Publish a release to PyPI. +# +name: "Publish to PyPI" + +on: + workflow_call: + inputs: + release-version: + required: true + type: string + dry-run: + required: true + type: boolean + +jobs: + pypi-publish: + name: Upload to PyPI ${{ inputs.release-version }} ${{ inputs.dry-run && '(dry-run)' || '' }} + runs-on: ubuntu-latest + if: ${{ !inputs.dry-run }} + permissions: + contents: read + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + pattern: pypi_files + path: dist + merge-multiple: true + + - uses: pdm-project/setup-pdm@v4 + with: + python-version: 3.12 + + - name: Publish package distributions to PyPI + run: pdm publish --no-build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e891e8a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,87 @@ +name: Release + +on: + push: + tags: + - 'v*' # Automatically trigger on version tags + - 'dry-run' + + workflow_dispatch: + inputs: + tag: + description: "Release Tag" + required: true + default: "dry-run" + type: string + +env: + PYTHON_VERSION: "3.12" + + +jobs: + plan: + runs-on: ubuntu-latest + outputs: + release_version: ${{ steps.release-version.outputs.release_version }} + dry-run: ${{ steps.release-version.outputs.dry_run }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set Release Version + id: release-version + run: | + if [ "${{ github.event_name }}" == "push" ]; then + echo "release_version=${{ github.ref_name }}" >> $GITHUB_OUTPUT + if [ "${{ github.ref_name }}" == "dry-run" ]; then + echo "dry_run=true" >> $GITHUB_OUTPUT + else + echo "dry_run=false" >> $GITHUB_OUTPUT + fi + else + version="${{ github.event.inputs.tag || 'dry-run' }}" + if [ "${version}" == "dry-run" ]; then + echo "release_version=latest" >> $GITHUB_OUTPUT + echo "dry_run=true" >> $GITHUB_OUTPUT + else + echo "release_version=${version}" >> $GITHUB_OUTPUT + echo "dry_run=false" >> $GITHUB_OUTPUT + fi + fi + - name: Display Release Version + run: echo "The release version is ${{ steps.release-version.outputs.release_version }}" + + unit-tests: + uses: ./.github/workflows/tests.yml + + build-artifacts: + needs: + - plan + - unit-tests + uses: ./.github/workflows/build-artifacts.yml + with: + release-version: ${{ needs.plan.outputs.release_version }} + dry-run: ${{ needs.plan.outputs.dry-run == 'true' }} + python-version: '3.12' + + tests-artifacts: + needs: + - plan + - build-artifacts + uses: ./.github/workflows/tests-artifacts.yml + with: + release-version: ${{ needs.plan.outputs.release_version }} + dry-run: ${{ needs.plan.outputs.dry-run == 'true' }} + + publish-pypi: + needs: + - plan + - tests-artifacts + uses: ./.github/workflows/publish-pypi.yml + with: + release-version: ${{ needs.plan.outputs.release_version }} + dry-run: ${{ needs.plan.outputs.dry-run == 'true' }} + permissions: + contents: read + id-token: write diff --git a/.github/workflows/tests-artifacts.yml b/.github/workflows/tests-artifacts.yml new file mode 100644 index 0000000..ea26f16 --- /dev/null +++ b/.github/workflows/tests-artifacts.yml @@ -0,0 +1,34 @@ +name: tests artifacts + +# Controls when the workflow will run +on: + # Allows you to run this workflow manually from the Actions tab + workflow_call: + inputs: + release-version: + required: true + type: string + description: "release number" + dry-run: + required: true + type: boolean + description: "blank run means that the release will not be pushed" + +jobs: + test-sdist: + name: test tarball archive of ${{ inputs.release-version }} ${{ inputs.dry-run && '(dry-run)' || '' }} + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + pattern: pypi_files + path: dist + merge-multiple: true + + - name: "Install" + run: | + pip install dist/celery_prometheus-*.whl --force-reinstall + + - name: "Test sdist" + run: | + python -c "from celery_prometheus import __version__; print(__version__, end='')" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 15a8174..a005562 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,55 +1,35 @@ name: tests on: - # Triggers the workflow on push or pull request events but only for the main branch push: branches: [ main ] pull_request: branches: [ main ] - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: + workflow_call: + jobs: - tests: - # The type of runner that the job will run on + CI: runs-on: ubuntu-latest - # Uncomment this part in case unit tests joins the party - # strategy: - # matrix: - # python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] - # poetry-version: ["1.4.0"] - # Steps represent a sequence of tasks that will be executed as part of the job + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - - - uses: actions/setup-python@v4 - with: - python-version: 3.8 - # python-version: ${{ matrix.python-version }} + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 - - name: Run image - uses: abatilo/actions-poetry@v2 + - uses: actions/setup-python@v5 with: - poetry-version: latest - # poetry-version: ${{ matrix.poetry-version }} - - - name: Setup the virtualenv for celery-prometheus - run: poetry install --with dev + python-version: ${{ matrix.python-version }} - - name: Check lint with Flake8 - run: | - poetry run flake8 . - - - name: Check imports with isort - run: | - poetry run isort -c . + - name: Install uv + uses: astral-sh/setup-uv@v3 - - name: Check formatting with black - run: | - poetry run black -C . + - name: Install the project + run: uv sync --group dev - - name: Check typing with mypy + - name: Check types run: | - poetry run mypy src/celery_prometheus/ + uv run mypy src/celery_prometheus/ diff --git a/Justfile b/Justfile index 524221b..9595b49 100644 --- a/Justfile +++ b/Justfile @@ -1,33 +1,41 @@ package := 'celery_prometheus' install: - poetry install --with dev + uv sync lint: - poetry run flake8 && echo "$(tput setaf 10)Success: no lint issue$(tput setaf 7)" + uv run ruff check . -mypy: - poetry run mypy src/ +typecheck: + uv run mypy src/ -black: - poetry run isort . - poetry run black . +fmt: + uv run ruff check --fix . + uv run ruff format src -release major_minor_patch: && changelog - poetry version {{major_minor_patch}} - poetry install +release major_minor_patch: changelog + #! /bin/bash + # Try to bump the version first + if ! uvx pdm bump {{major_minor_patch}}; then + # If it fails, check if pdm-bump is installed + if ! uvx pdm self list | grep -q pdm-bump; then + # If not installed, add pdm-bump + uvx pdm self add pdm-bump + fi + # Attempt to bump the version again + uvx pdm bump {{major_minor_patch}} + fi + uv sync changelog: - poetry run python scripts/write_changelog.py + uv run python scripts/write_changelog.py cat CHANGELOG.md >> CHANGELOG.md.new rm CHANGELOG.md mv CHANGELOG.md.new CHANGELOG.md $EDITOR CHANGELOG.md publish: - git commit -am "Release $(poetry version -s)" - poetry build - poetry publish + git commit -am "Release $(uv run scripts/get_version.py)" git push - git tag "$(poetry version -s)" - git push origin "$(poetry version -s)" + git tag "v$(uv run scripts/get_version.py)" + git push origin "v$(uv run scripts/get_version.py)" diff --git a/pyproject.toml b/pyproject.toml index e9f766d..d78c2e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,31 +1,37 @@ -[tool.poetry] +[project] name = "celery-prometheus" -version = "1.1.0" +version = "2.1.2" description = "Celery with your own prometheus metrics" -authors = ["Guillaume Gauvrit "] +authors = [{ name = "Guillaume Gauvrit", email = "guillaume@gandi.net" }] readme = "README.md" -license = "BSD-Derived" +requires-python = ">=3.9" +dependencies = ["celery>=5.4.0,<6", "prometheus-client >=0.17,<1"] -[tool.poetry.dependencies] -python = "^3.7" -celery = ">=4" - -[tool.poetry.group.dev.dependencies] -black = "^23.1.0" -flake8 = "5.0.4" -isort = "5.11.4" -mypy = "1.0.0" -prometheus-client = "^0.16.0" -types-setuptools = "^67.4.0.3" +[dependency-groups] +dev = ["celery>=5.4.0,<6", "mypy>=1.13.0,<2"] [[tool.mypy.overrides]] disallow_any_generics = true disallow_untyped_defs = true module = "celery_prometheus.*" -[tool.isort] -profile = "black" +[[tool.mypy.overrides]] +module = ["celery.*"] +ignore_missing_imports = true + +[tool.ruff] +line-length = 88 +target-version = "py39" + +[tool.ruff.lint] +select = [ + "B", # bug bear security warning + "I", # isort import order + "F", # pyflakes + "UP", # alter when better syntax is available + "RUF", # the ruff devleoper's own rules +] [build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +requires = ["pdm-backend"] +build-backend = "pdm.backend" diff --git a/scripts/get_version.py b/scripts/get_version.py new file mode 100644 index 0000000..7842f74 --- /dev/null +++ b/scripts/get_version.py @@ -0,0 +1,4 @@ +from celery_prometheus import __version__ + +if __name__ == "__main__": + print(__version__, end="") diff --git a/src/celery_prometheus/__init__.py b/src/celery_prometheus/__init__.py index 36eb062..c4431f9 100644 --- a/src/celery_prometheus/__init__.py +++ b/src/celery_prometheus/__init__.py @@ -1,10 +1,7 @@ -import pkg_resources - -try: - __version__ = pkg_resources.get_distribution("celery_prometheus").version -except pkg_resources.DistributionNotFound: - pass +import importlib.metadata from .prometheus_bootstep import add_prometheus_option +__version__ = importlib.metadata.version("celery-prometheus") + __all__ = ["add_prometheus_option"] diff --git a/src/celery_prometheus/prometheus_bootstep.py b/src/celery_prometheus/prometheus_bootstep.py index d77b057..ca1f0c3 100644 --- a/src/celery_prometheus/prometheus_bootstep.py +++ b/src/celery_prometheus/prometheus_bootstep.py @@ -1,62 +1,39 @@ """Helper for celery.""" + import logging import os -from argparse import ArgumentParser -from typing import Any, Mapping, Optional, cast +from typing import Any, Optional -from celery import VERSION as celery_version # type: ignore -from celery import Celery +from celery import Celery, bootsteps +from click import Option from prometheus_client import CollectorRegistry, start_http_server from prometheus_client.multiprocess import MultiProcessCollector log = logging.getLogger(__name__) -if celery_version.major < 5: - from celery.signals import user_preload_options # type: ignore - - @user_preload_options.connect - def on_preload_parsed(options: Mapping[str, Any], **kwargs: Any) -> None: - prometheus_collector_addr = options.get("prometheus_collector_addr") - app = cast(Celery, kwargs["app"]) - attach_prometheus_registry(app, prometheus_collector_addr) - - def add_prometheus_option(app: Celery) -> None: help = "Celery Prometheus Configuration." - if celery_version.major < 5: - def add_preload_arguments(parser: ArgumentParser) -> None: - parser.add_argument( - "--prometheus-collector-addr", - default=os.getenv("CELERY_PROMETHEUS_COLLECTOR_ADDR"), - help=help, - ) - - app.user_options["preload"].add(add_preload_arguments) - else: - from celery import bootsteps - from click import Option - - app.user_options["preload"].add( - Option( - ["--prometheus-collector-addr"], - required=False, - help=help, - default=os.getenv("CELERY_PROMETHEUS_COLLECTOR_ADDR"), - ) + app.user_options["preload"].add( + Option( + ["--prometheus-collector-addr"], + required=False, + help=help, + default=os.getenv("CELERY_PROMETHEUS_COLLECTOR_ADDR"), ) - - class PrometheusBootstep(bootsteps.Step): - def __init__( - self, - parent: bootsteps.Step, - prometheus_collector_addr: str = "", - **options: Any, - ) -> None: - attach_prometheus_registry(app, prometheus_collector_addr) - - app.steps["worker"].add(PrometheusBootstep) + ) + + class PrometheusBootstep(bootsteps.Step): + def __init__( + self, + parent: bootsteps.Step, + prometheus_collector_addr: str = "", + **options: Any, + ) -> None: + attach_prometheus_registry(app, prometheus_collector_addr) + + app.steps["worker"].add(PrometheusBootstep) def attach_prometheus_registry(app: Celery, prometheus_addr: Optional[str]) -> None: diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..a3f2896 --- /dev/null +++ b/uv.lock @@ -0,0 +1,278 @@ +version = 1 +requires-python = ">=3.9" + +[[package]] +name = "amqp" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "vine" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/fc/ec94a357dfc6683d8c86f8b4cfa5416a4c36b28052ec8260c77aca96a443/amqp-5.3.1.tar.gz", hash = "sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432", size = 129013 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/99/fc813cd978842c26c82534010ea849eee9ab3a13ea2b74e95cb9c99e747b/amqp-5.3.1-py3-none-any.whl", hash = "sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2", size = 50944 }, +] + +[[package]] +name = "billiard" +version = "4.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/58/1546c970afcd2a2428b1bfafecf2371d8951cc34b46701bea73f4280989e/billiard-4.2.1.tar.gz", hash = "sha256:12b641b0c539073fc8d3f5b8b7be998956665c4233c7c1fcd66a7e677c4fb36f", size = 155031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/da/43b15f28fe5f9e027b41c539abc5469052e9d48fd75f8ff094ba2a0ae767/billiard-4.2.1-py3-none-any.whl", hash = "sha256:40b59a4ac8806ba2c2369ea98d876bc6108b051c227baffd928c644d15d8f3cb", size = 86766 }, +] + +[[package]] +name = "celery" +version = "5.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "billiard" }, + { name = "click" }, + { name = "click-didyoumean" }, + { name = "click-plugins" }, + { name = "click-repl" }, + { name = "kombu" }, + { name = "python-dateutil" }, + { name = "tzdata" }, + { name = "vine" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/9c/cf0bce2cc1c8971bf56629d8f180e4ca35612c7e79e6e432e785261a8be4/celery-5.4.0.tar.gz", hash = "sha256:504a19140e8d3029d5acad88330c541d4c3f64c789d85f94756762d8bca7e706", size = 1575692 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/c4/6a4d3772e5407622feb93dd25c86ce3c0fee746fa822a777a627d56b4f2a/celery-5.4.0-py3-none-any.whl", hash = "sha256:369631eb580cf8c51a82721ec538684994f8277637edde2dfc0dacd73ed97f64", size = 425983 }, +] + +[[package]] +name = "celery-prometheus" +version = "2.1.2" +source = { editable = "." } +dependencies = [ + { name = "celery" }, + { name = "prometheus-client" }, +] + +[package.dev-dependencies] +dev = [ + { name = "celery" }, + { name = "mypy" }, +] + +[package.metadata] +requires-dist = [ + { name = "celery", specifier = ">=5.4.0,<6" }, + { name = "prometheus-client", specifier = ">=0.17,<1" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "celery", specifier = ">=5.4.0,<6" }, + { name = "mypy", specifier = ">=1.13.0,<2" }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[package]] +name = "click-didyoumean" +version = "0.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/30/ce/217289b77c590ea1e7c24242d9ddd6e249e52c795ff10fac2c50062c48cb/click_didyoumean-0.3.1.tar.gz", hash = "sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463", size = 3089 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/5b/974430b5ffdb7a4f1941d13d83c64a0395114503cc357c6b9ae4ce5047ed/click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c", size = 3631 }, +] + +[[package]] +name = "click-plugins" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/1d/45434f64ed749540af821fd7e42b8e4d23ac04b1eda7c26613288d6cd8a8/click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", size = 8164 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/da/824b92d9942f4e472702488857914bdd50f73021efea15b4cad9aca8ecef/click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8", size = 7497 }, +] + +[[package]] +name = "click-repl" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "prompt-toolkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/a2/57f4ac79838cfae6912f997b4d1a64a858fb0c86d7fcaae6f7b58d267fca/click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9", size = 10449 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/40/9d857001228658f0d59e97ebd4c346fe73e138c6de1bce61dc568a57c7f8/click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812", size = 10289 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "kombu" +version = "5.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "amqp" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, + { name = "tzdata" }, + { name = "vine" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/4d/b93fcb353d279839cc35d0012bee805ed0cf61c07587916bfc35dbfddaf1/kombu-5.4.2.tar.gz", hash = "sha256:eef572dd2fd9fc614b37580e3caeafdd5af46c1eff31e7fba89138cdb406f2cf", size = 442858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/ec/7811a3cf9fdfee3ee88e54d08fcbc3fabe7c1b6e4059826c59d7b795651c/kombu-5.4.2-py3-none-any.whl", hash = "sha256:14212f5ccf022fc0a70453bb025a1dcc32782a588c49ea866884047d66e14763", size = 201349 }, +] + +[[package]] +name = "mypy" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/8c/206de95a27722b5b5a8c85ba3100467bd86299d92a4f71c6b9aa448bfa2f/mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", size = 11020731 }, + { url = "https://files.pythonhosted.org/packages/ab/bb/b31695a29eea76b1569fd28b4ab141a1adc9842edde080d1e8e1776862c7/mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", size = 10184276 }, + { url = "https://files.pythonhosted.org/packages/a5/2d/4a23849729bb27934a0e079c9c1aad912167d875c7b070382a408d459651/mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", size = 12587706 }, + { url = "https://files.pythonhosted.org/packages/5c/c3/d318e38ada50255e22e23353a469c791379825240e71b0ad03e76ca07ae6/mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", size = 13105586 }, + { url = "https://files.pythonhosted.org/packages/4a/25/3918bc64952370c3dbdbd8c82c363804678127815febd2925b7273d9482c/mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", size = 9632318 }, + { url = "https://files.pythonhosted.org/packages/d0/19/de0822609e5b93d02579075248c7aa6ceaddcea92f00bf4ea8e4c22e3598/mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", size = 10939027 }, + { url = "https://files.pythonhosted.org/packages/c8/71/6950fcc6ca84179137e4cbf7cf41e6b68b4a339a1f5d3e954f8c34e02d66/mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", size = 10108699 }, + { url = "https://files.pythonhosted.org/packages/26/50/29d3e7dd166e74dc13d46050b23f7d6d7533acf48f5217663a3719db024e/mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", size = 12506263 }, + { url = "https://files.pythonhosted.org/packages/3f/1d/676e76f07f7d5ddcd4227af3938a9c9640f293b7d8a44dd4ff41d4db25c1/mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", size = 12984688 }, + { url = "https://files.pythonhosted.org/packages/9c/03/5a85a30ae5407b1d28fab51bd3e2103e52ad0918d1e68f02a7778669a307/mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", size = 9626811 }, + { url = "https://files.pythonhosted.org/packages/fb/31/c526a7bd2e5c710ae47717c7a5f53f616db6d9097caf48ad650581e81748/mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", size = 11077900 }, + { url = "https://files.pythonhosted.org/packages/83/67/b7419c6b503679d10bd26fc67529bc6a1f7a5f220bbb9f292dc10d33352f/mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", size = 10074818 }, + { url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 }, + { url = "https://files.pythonhosted.org/packages/1f/17/b1018c6bb3e9f1ce3956722b3bf91bff86c1cefccca71cec05eae49d6d41/mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", size = 13037783 }, + { url = "https://files.pythonhosted.org/packages/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 }, + { url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, + { url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, + { url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, + { url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, + { url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, + { url = "https://files.pythonhosted.org/packages/5f/d4/b33ddd40dad230efb317898a2d1c267c04edba73bc5086bf77edeb410fb2/mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc", size = 11013906 }, + { url = "https://files.pythonhosted.org/packages/f4/e6/f414bca465b44d01cd5f4a82761e15044bedd1bf8025c5af3cc64518fac5/mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732", size = 10180657 }, + { url = "https://files.pythonhosted.org/packages/38/e9/fc3865e417722f98d58409770be01afb961e2c1f99930659ff4ae7ca8b7e/mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc", size = 12586394 }, + { url = "https://files.pythonhosted.org/packages/2e/35/f4d8b6d2cb0b3dad63e96caf159419dda023f45a358c6c9ac582ccaee354/mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d", size = 13103591 }, + { url = "https://files.pythonhosted.org/packages/22/1d/80594aef135f921dd52e142fa0acd19df197690bd0cde42cea7b88cf5aa2/mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24", size = 9634690 }, + { url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "prometheus-client" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/54/a369868ed7a7f1ea5163030f4fc07d85d22d7a1d270560dab675188fb612/prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e", size = 78634 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/2d/46ed6436849c2c88228c3111865f44311cff784b4aabcdef4ea2545dbc3d/prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166", size = 54686 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/4f/feb5e137aff82f7c7f3248267b97451da3644f6cdc218edfe549fb354127/prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90", size = 424684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "tomli" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/e4/1b6cbcc82d8832dd0ce34767d5c560df8a3547ad8cbc427f34601415930a/tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8", size = 16622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/f7/4da0ffe1892122c9ea096c57f64c2753ae5dd3ce85488802d11b0992cc6d/tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391", size = 13750 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, +] + +[[package]] +name = "vine" +version = "5.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/e4/d07b5f29d283596b9727dd5275ccbceb63c44a1a82aa9e4bfd20426762ac/vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0", size = 48980 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/ff/7c0c86c43b3cbb927e0ccc0255cb4057ceba4799cd44ae95174ce8e8b5b2/vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc", size = 9636 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +]