├── tests ├── __init__.py ├── __snapshots__ │ ├── test_entrypoint │ │ ├── test_filtering[exclude-all].sh │ │ ├── test_filtering[no-filter].sh │ │ ├── test_serialization[no_script].sh │ │ ├── test_pythonpath_explicit_src_layout.sh │ │ ├── test_pythonpath_implicit_src_layout.sh │ │ ├── test_serialization[shell].sh │ │ ├── test_serialization[cmd-as-dict].sh │ │ ├── test_serialization[cmd-as-str].sh │ │ ├── test_serialization[call].sh │ │ ├── test_serialization[composite].sh │ │ ├── test_serialization[cmd-as-list].sh │ │ ├── test_serialization[composite-inline].sh │ │ ├── test_global_env.sh │ │ ├── test_serialization[call-with-arguments].sh │ │ ├── test_filtering[include-list].sh │ │ ├── test_serialization[env].sh │ │ ├── test_filtering[include-prefix-except].sh │ │ ├── test_serialization[shared-env].sh │ │ ├── test_filtering[include-all-but-prefix].sh │ │ ├── test_global_env_file.sh │ │ ├── test_serialization[env_file].sh │ │ ├── test_serialization[shared-env_file].sh │ │ ├── test_serialization[env_file-override].sh │ │ ├── test_serialization[pre-post].sh │ │ ├── test_filtering[exclude-list].sh │ │ ├── test_serialization[env_file-precedance].sh │ │ ├── test_serialization[env_file-override-precedance].sh │ │ ├── test_serialization[whitespaces].sh │ │ ├── test_serialization[args-placeholder].sh │ │ ├── test_filtering[include-all].sh │ │ └── test_serialization[args-placeholder-with-defaults].sh │ └── test_pdm_dockerize │ │ ├── test_generate_docker_dist.sh │ │ └── test_generate_docker_dist_to_target.sh ├── test_filters.py ├── conftest.py ├── test_pdm_dockerize.py └── test_entrypoint.py ├── src └── pdm_dockerize │ ├── py.typed │ ├── __init__.py │ ├── filters.py │ ├── config.py │ ├── commands.py │ ├── installer.py │ └── entrypoint.py ├── .gitattributes ├── .markdownlint.yaml ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .copier-answers.yml ├── tox.ini ├── LICENSE ├── .pre-commit-config.yaml ├── .gitignore ├── CHANGELOG.md ├── pyproject.toml ├── README.md └── pdm.lock /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pdm_dockerize/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.lock -diff 2 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | MD024: 3 | exclude: 4 | - "CHANGELOG.md" 5 | -------------------------------------------------------------------------------- /src/pdm_dockerize/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import importlib.metadata as importlib_metadata 4 | 5 | from pdm.core import Core 6 | 7 | __version__ = importlib_metadata.version("pdm-dockerize") 8 | 9 | 10 | def plugin(core: Core): 11 | from .commands import DockerizeCommand 12 | 13 | # Register commands 14 | core.register_command(DockerizeCommand, "dockerize") 15 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_filtering[exclude-all].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | } 19 | 20 | case $cmd in 21 | *) 22 | usage 23 | ;; 24 | esac 25 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_filtering[no-filter].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | } 19 | 20 | case $cmd in 21 | *) 22 | usage 23 | ;; 24 | esac 25 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[no_script].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | } 19 | 20 | case $cmd in 21 | *) 22 | usage 23 | ;; 24 | esac 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.py] 14 | indent_size = 4 15 | max_line_length = 110 16 | 17 | [*.{yml,yaml,toml,json,jsonc,json5,hjson,ini}] 18 | indent_size = 2 19 | 20 | [*.md] 21 | indent_size = 2 22 | 23 | [*.{js,css}] 24 | indent_size = 2 25 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_pythonpath_explicit_src_layout.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/src":"$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | } 19 | 20 | case $cmd in 21 | *) 22 | usage 23 | ;; 24 | esac 25 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_pythonpath_implicit_src_layout.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/src":"$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | } 19 | 20 | case $cmd in 21 | *) 22 | usage 23 | ;; 24 | esac 25 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[shell].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | pytest "$@" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[cmd-as-dict].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | pytest "$@" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[cmd-as-str].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | pytest "$@" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_pdm_dockerize/test_generate_docker_dist.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | pytest "$@" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_pdm_dockerize/test_generate_docker_dist_to_target.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | pytest "$@" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[call].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: my.app:main" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | python -c "from my.app import main; main()" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[composite].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: first ➤ second" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | first "$@" 24 | second "$@" 25 | ;; 26 | *) 27 | usage 28 | ;; 29 | esac 30 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[cmd-as-list].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest --with --params" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | pytest --with --params "$@" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - 4 | # Maintain dependencies for GitHub Actions 5 | package-ecosystem: github-actions 6 | directory: / 7 | schedule: 8 | interval: daily 9 | labels: 10 | - dependencies 11 | - ci 12 | commit-message: 13 | prefix: "ci" 14 | include: "scope" 15 | - 16 | # Maintain python dependencies 17 | package-ecosystem: pip 18 | directory: / 19 | schedule: 20 | interval: daily 21 | labels: 22 | - dependencies 23 | commit-message: 24 | prefix: "build" 25 | include: "scope" 26 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[composite-inline].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "command: _helper something" 19 | } 20 | 21 | case $cmd in 22 | command) 23 | should be inlined something "$@" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_global_env.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | VAR="42" 15 | export VAR 16 | LAST="value" 17 | export LAST 18 | 19 | usage() { 20 | echo "Available commands" 21 | echo "==================" 22 | echo "hello: echo 'Hello'" 23 | } 24 | 25 | case $cmd in 26 | hello) 27 | echo 'Hello' "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[call-with-arguments].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: my.app:main('dev', key='value')" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | python -c "from my.app import main; main('dev', key='value')" 24 | ;; 25 | *) 26 | usage 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_filtering[include-list].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | echo "ns:task1: ns:task1" 20 | } 21 | 22 | case $cmd in 23 | test) 24 | pytest "$@" 25 | ;; 26 | ns:task1) 27 | ns:task1 "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[env].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | WHATEVER="42" 24 | export WHATEVER 25 | OTHER="value" 26 | export OTHER 27 | pytest "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_filtering[include-prefix-except].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "ns:task2: ns:task2" 19 | echo "ns:task3: ns:task3" 20 | } 21 | 22 | case $cmd in 23 | ns:task2) 24 | ns:task2 "$@" 25 | ;; 26 | ns:task3) 27 | ns:task3 "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[shared-env].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | WHATEVER="42" 24 | export WHATEVER 25 | OTHER="new-value" 26 | export OTHER 27 | pytest "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /src/pdm_dockerize/filters.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from fnmatch import fnmatch 4 | from typing import Mapping 5 | 6 | 7 | def parse(data: Mapping, key: str) -> list[str]: 8 | """Parse a filter key""" 9 | filters = data.get(key) or [] 10 | if isinstance(filters, str): 11 | filters = [filters] 12 | if any(not isinstance(filter, str) for filter in filters): 13 | raise TypeError("Filters should be fnmatch patterns as string") 14 | return filters 15 | 16 | 17 | def match(value: str, patterns: list[str]) -> bool: 18 | """Check whether a value match any of the patterns""" 19 | return any(fnmatch(value, pattern) for pattern in patterns) 20 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_filtering[include-all-but-prefix].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | echo "test:something: pytest something" 20 | } 21 | 22 | case $cmd in 23 | test) 24 | pytest "$@" 25 | ;; 26 | test:something) 27 | pytest something "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /.copier-answers.yml: -------------------------------------------------------------------------------- 1 | # Changes here will be overwritten by Copier 2 | _commit: c9511f3 3 | _src_path: copier.templates/python.copier 4 | author_email: noirbizarre@gmail.com 5 | author_fullname: Axel Haustant 6 | author_username: noirbizarre 7 | copyright_license: MIT License 8 | has_docs: false 9 | is_cli: false 10 | is_lib: true 11 | is_private: false 12 | project_description: Help generating docker image from PDM projects 13 | project_name: pdm-dockerize 14 | project_slug: pdm-dockerize 15 | python_distribution: pdm-dockerize 16 | python_package: pdm_dockerize 17 | repository_name: pdm-dockerize 18 | repository_namespace: noirbizarre 19 | repository_provider: github.com 20 | use_src: true 21 | 22 | # End 23 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_global_env_file.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | set -o allexport 15 | # shellcheck source=/dev/null 16 | [ -f docker.env ] && . docker.env || echo 'docker.env is ignored as it does not exist.' 17 | set +o allexport 18 | 19 | usage() { 20 | echo "Available commands" 21 | echo "==================" 22 | echo "hello: echo 'Hello'" 23 | } 24 | 25 | case $cmd in 26 | hello) 27 | echo 'Hello' "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[env_file].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | set -o allexport 24 | # shellcheck source=/dev/null 25 | [ -f .env ] && . .env || echo '.env is ignored as it does not exist.' 26 | set +o allexport 27 | pytest "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /src/pdm_dockerize/config.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any, TypedDict 4 | 5 | 6 | class DockerizeSettings(TypedDict): 7 | include: str | list[str] | None 8 | """List of command patterns to include""" 9 | 10 | exclude: str | list[str] | None 11 | """List of command patterns to exclude""" 12 | 13 | include_bins: str | list[str] | None 14 | """List of binaries patterns to include""" 15 | 16 | exclude_bins: str | list[str] | None 17 | """List of binanries patterns to exclude""" 18 | 19 | env: dict[str, Any] | None 20 | """Global environment variables to export in docker only""" 21 | 22 | env_file: str | None 23 | """Global dotenv file to source to source in docker only""" 24 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[shared-env_file].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | set -o allexport 24 | # shellcheck source=/dev/null 25 | [ -f .env ] && . .env || echo '.env is ignored as it does not exist.' 26 | set +o allexport 27 | pytest "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[env_file-override].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | set -o allexport 24 | # shellcheck source=/dev/null 25 | [ -f .env ] && . .env || echo '.env is ignored as it does not exist.' 26 | set +o allexport 27 | pytest "$@" 28 | ;; 29 | *) 30 | usage 31 | ;; 32 | esac 33 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - "*.md" 7 | push: 8 | branches: 9 | - main 10 | paths-ignore: 11 | - "*.md" 12 | workflow_dispatch: 13 | 14 | jobs: 15 | tests: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up PDM 24 | uses: pdm-project/setup-pdm@v4 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - name: Install dependencies 29 | run: pdm sync -d -G test 30 | 31 | - name: Run Tests 32 | run: pdm cover -v --force-sugar --color=yes 33 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[pre-post].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "pre_test: pre" 19 | echo "test: pytest" 20 | echo "post_test: post" 21 | } 22 | 23 | case $cmd in 24 | pre_test) 25 | pre "$@" 26 | ;; 27 | test) 28 | pre "$@" 29 | pytest "$@" 30 | post "$@" 31 | ;; 32 | post_test) 33 | post "$@" 34 | ;; 35 | *) 36 | usage 37 | ;; 38 | esac 39 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_filtering[exclude-list].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test:something: pytest something" 19 | echo "ns:task2: ns:task2" 20 | echo "ns:task3: ns:task3" 21 | } 22 | 23 | case $cmd in 24 | test:something) 25 | pytest something "$@" 26 | ;; 27 | ns:task2) 28 | ns:task2 "$@" 29 | ;; 30 | ns:task3) 31 | ns:task3 "$@" 32 | ;; 33 | *) 34 | usage 35 | ;; 36 | esac 37 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[env_file-precedance].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | set -o allexport 24 | # shellcheck source=/dev/null 25 | [ -f .env ] && . .env || echo '.env is ignored as it does not exist.' 26 | set +o allexport 27 | WHATEVER="42" 28 | export WHATEVER 29 | pytest "$@" 30 | ;; 31 | *) 32 | usage 33 | ;; 34 | esac 35 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[env_file-override-precedance].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | } 20 | 21 | case $cmd in 22 | test) 23 | WHATEVER="42" 24 | export WHATEVER 25 | set -o allexport 26 | # shellcheck source=/dev/null 27 | [ -f .env ] && . .env || echo '.env is ignored as it does not exist.' 28 | set +o allexport 29 | pytest "$@" 30 | ;; 31 | *) 32 | usage 33 | ;; 34 | esac 35 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py3{8,9,10,11,12},report 8 | isolated_build = true 9 | setenv = 10 | COVERAGE_FILE = {toxworkdir}/coverage 11 | 12 | [testenv] 13 | groups = test 14 | usedevelop = True 15 | commands = 16 | coverage run --append -m pytest {posargs} --junitxml=reports/tests.{envname}.xml 17 | 18 | [testenv:report] 19 | deps = coverage 20 | skip_install = true 21 | parallel_show_output = true 22 | depends = py3{8,9,10,11,12} 23 | commands = 24 | coverage report 25 | coverage xml -o reports/tox/coverage.xml 26 | coverage html -d reports/tox/coverage 27 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[whitespaces].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "cmd: whitespaces…" 19 | echo "shell: whitespaces…" 20 | echo "composite: whitespaces…" 21 | } 22 | 23 | case $cmd in 24 | cmd) 25 | whitespaces are ignored "$@" 26 | ;; 27 | shell) 28 | whitespaces 29 | should be 30 | preserved "$@" 31 | ;; 32 | composite) 33 | whitespaces are ignored "$@" 34 | ;; 35 | *) 36 | usage 37 | ;; 38 | esac 39 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[args-placeholder].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "cmd: cmd.before {args} cmd.after" 19 | echo "shell: shell.before {args} shell.after" 20 | echo "composite: cmd --something ➤ shell {args}" 21 | } 22 | 23 | case $cmd in 24 | cmd) 25 | cmd.before "$@" cmd.after 26 | ;; 27 | shell) 28 | shell.before "$@" shell.after 29 | ;; 30 | composite) 31 | cmd.before "$@" cmd.after --something 32 | shell.before "$@" shell.after "$@" 33 | ;; 34 | *) 35 | usage 36 | ;; 37 | esac 38 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_filtering[include-all].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "test: pytest" 19 | echo "test:something: pytest something" 20 | echo "ns:task1: ns:task1" 21 | echo "ns:task2: ns:task2" 22 | echo "ns:task3: ns:task3" 23 | } 24 | 25 | case $cmd in 26 | test) 27 | pytest "$@" 28 | ;; 29 | test:something) 30 | pytest something "$@" 31 | ;; 32 | ns:task1) 33 | ns:task1 "$@" 34 | ;; 35 | ns:task2) 36 | ns:task2 "$@" 37 | ;; 38 | ns:task3) 39 | ns:task3 "$@" 40 | ;; 41 | *) 42 | usage 43 | ;; 44 | esac 45 | -------------------------------------------------------------------------------- /tests/__snapshots__/test_entrypoint/test_serialization[args-placeholder-with-defaults].sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | dirname=$(dirname "$0") 6 | cmd=${1:-""} 7 | [ "$cmd" ] && shift 8 | cd "$dirname" > /dev/null 9 | 10 | PYTHONPATH="$(pwd)/lib" 11 | export PYTHONPATH 12 | PATH="$(pwd)/bin":"$PATH" 13 | export PATH 14 | 15 | usage() { 16 | echo "Available commands" 17 | echo "==================" 18 | echo "cmd: cmd.before {args:default value} cmd.after" 19 | echo "shell: shell.before {args:default value} shell.after" 20 | echo "composite: cmd --something ➤ shell {args:default value}" 21 | } 22 | 23 | case $cmd in 24 | cmd) 25 | cmd.before "${@:-default value}" cmd.after 26 | ;; 27 | shell) 28 | shell.before "${@:-default value}" shell.after 29 | ;; 30 | composite) 31 | cmd.before "${@:-default value}" cmd.after --something 32 | shell.before "${@:-default value}" shell.after "${@:-default value}" 33 | ;; 34 | *) 35 | usage 36 | ;; 37 | esac 38 | -------------------------------------------------------------------------------- /tests/test_filters.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from dataclasses import dataclass 4 | from typing import Any 5 | 6 | import pytest 7 | 8 | from pdm_dockerize import filters 9 | 10 | 11 | @dataclass 12 | class FilterCase: 13 | id: str 14 | data: Any 15 | expected: list[str] | None = None 16 | error: type[Exception] | None = None 17 | 18 | 19 | FILTER_CASES = ( 20 | FilterCase("no-filter", None, []), 21 | FilterCase("str", "*", ["*"]), 22 | FilterCase("empty-str", "", []), 23 | FilterCase("list", ["some", "filters"], ["some", "filters"]), 24 | FilterCase("empty-list", [], []), 25 | FilterCase("bad-type", 42, error=TypeError), 26 | FilterCase("bad-inner-type", ["OK", 42], error=TypeError), 27 | ) 28 | 29 | 30 | @pytest.mark.parametrize( 31 | "case", 32 | ( 33 | pytest.param( 34 | case, id=case.id, marks=pytest.mark.xfail(raises=case.error) if case.error else () 35 | ) 36 | for case in FILTER_CASES 37 | ), 38 | ) 39 | def test_parse_filters(case: FilterCase): 40 | assert filters.parse({"key": case.data}, "key") == case.expected 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Axel Haustant 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_install_hook_types: 2 | - pre-commit 3 | - commit-msg 4 | - pre-push 5 | - post-checkout 6 | - post-merge 7 | 8 | default_stages: 9 | - pre-commit 10 | 11 | exclude: ^CHANGELOG\.md$ 12 | 13 | repos: 14 | - repo: meta 15 | hooks: 16 | - id: check-hooks-apply 17 | 18 | - repo: https://github.com/pre-commit/pre-commit-hooks 19 | rev: v4.6.0 20 | hooks: 21 | - id: trailing-whitespace 22 | args: [--markdown-linebreak-ext=md] 23 | - id: end-of-file-fixer 24 | exclude: "tests/data/.+" 25 | - id: check-yaml 26 | - id: check-added-large-files 27 | - id: debug-statements 28 | - id: check-toml 29 | - id: detect-private-key 30 | 31 | - repo: https://github.com/pdm-project/pdm 32 | rev: 2.19.0 33 | hooks: 34 | - id: pdm-lock-check 35 | - id: pdm-sync 36 | 37 | - repo: https://github.com/commitizen-tools/commitizen 38 | rev: v3.28.0 39 | hooks: 40 | - id: commitizen 41 | additional_dependencies: 42 | - emotional==0.5.1 43 | 44 | - repo: https://github.com/python-jsonschema/check-jsonschema 45 | rev: 0.28.6 46 | hooks: 47 | - id: check-github-workflows 48 | - id: check-dependabot 49 | 50 | - repo: https://github.com/astral-sh/ruff-pre-commit 51 | rev: v0.5.4 52 | hooks: 53 | - id: ruff 54 | args: [--fix] 55 | 56 | - repo: https://github.com/pre-commit/mirrors-mypy 57 | rev: v1.11.0 58 | hooks: 59 | - id: mypy 60 | exclude: docs/.*\.pyi?$ 61 | 62 | - repo: https://github.com/codespell-project/codespell 63 | rev: v2.3.0 64 | hooks: 65 | - id: codespell 66 | additional_dependencies: 67 | - tomli==2.0.1 68 | args: ["--write-changes"] 69 | exclude: CHANGELOG\.md 70 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import subprocess 4 | from pathlib import Path 5 | from typing import TYPE_CHECKING, Protocol 6 | 7 | import pytest 8 | from syrupy.extensions.single_file import SingleFileSnapshotExtension, WriteMode 9 | 10 | if TYPE_CHECKING: 11 | from pdm.project import Project 12 | from syrupy import SnapshotAssertion 13 | 14 | 15 | ROOT = Path(__file__).parent / "tests" 16 | 17 | pytest_plugins = [ 18 | "pdm.pytest", 19 | ] 20 | 21 | 22 | @pytest.fixture 23 | def project(project: Project, request: pytest.FixtureRequest) -> Project: 24 | if marker := request.node.get_closest_marker("pdm_global_config"): 25 | for key, value in marker.kwargs.items(): 26 | project.global_config[key] = value 27 | if marker := request.node.get_closest_marker("pdm_local_config"): 28 | for key, value in marker.kwargs.items(): 29 | project.project_config[key] = value 30 | return project 31 | 32 | 33 | class ScriptExtension(SingleFileSnapshotExtension): 34 | _file_extension = "sh" 35 | _write_mode = WriteMode.TEXT 36 | 37 | 38 | @pytest.fixture 39 | def snapshot(snapshot: SnapshotAssertion) -> SnapshotAssertion: 40 | return snapshot.use_extension(ScriptExtension) 41 | 42 | 43 | class ShellcheckFixture(Protocol): 44 | def __call__(self, script: str): ... 45 | 46 | 47 | class ShellcheckError(AssertionError): 48 | pass 49 | 50 | 51 | @pytest.fixture 52 | def shellcheck(tmp_path_factory: pytest.TempPathFactory) -> ShellcheckFixture: 53 | def fixture(script: str): 54 | file = tmp_path_factory.mktemp("shellcheck", True) / "script.sh" 55 | file.write_text(script) 56 | result = subprocess.run(["shellcheck", str(file)], capture_output=True) 57 | file.unlink() 58 | if result.returncode != 0: 59 | raise ShellcheckError(result.stdout.decode()) 60 | 61 | return fixture 62 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | increment: 7 | type: choice 8 | description: Kind of increment (optional) 9 | options: 10 | - '' 11 | - MAJOR 12 | - MINOR 13 | - PATCH 14 | default: '' 15 | required: false 16 | 17 | jobs: 18 | release: 19 | name: Bump version and create changelog with commitizen 20 | runs-on: ubuntu-latest 21 | # IMPORTANT: both environment and the id-token permission are mandatory for trusted publishing 22 | environment: releases 23 | permissions: 24 | id-token: write 25 | 26 | steps: 27 | - name: Ensures ref to release pass CI 28 | uses: noirbizarre/need-checks@0.1 29 | with: 30 | workflow: ci.yml 31 | token: ${{ secrets.PAT }} 32 | 33 | - name: Clone 34 | uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 37 | token: ${{ secrets.PAT }} 38 | 39 | - name: Set up PDM 40 | uses: pdm-project/setup-pdm@v4 41 | with: 42 | python-version: "3.11" 43 | cache: true 44 | 45 | - name: Bump using commitizen 46 | uses: commitizen-tools/commitizen-action@0.21.0 47 | with: 48 | github_token: ${{ secrets.PAT }} 49 | changelog_increment_filename: body.md 50 | extra_requirements: emotional 51 | increment: ${{ github.event.inputs.increment }} 52 | 53 | - name: Build artifacts 54 | run: pdm build 55 | 56 | - name: Github Release 57 | id: github-release 58 | uses: softprops/action-gh-release@v2 59 | with: 60 | body_path: "body.md" 61 | tag_name: ${{ env.REVISION }} 62 | files: dist/* 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.PAT }} 65 | 66 | - name: Publish on PyPI 67 | run: pdm publish --no-build 68 | 69 | - name: Publish summary 70 | run: | 71 | cat body.md >> $GITHUB_STEP_SUMMARY 72 | echo "" >> $GITHUB_STEP_SUMMARY 73 | echo "### Artifacts" >> $GITHUB_STEP_SUMMARY 74 | echo "- GitHub release: ${{ steps.github-release.outputs.url }}" >> $GITHUB_STEP_SUMMARY 75 | echo "- PyPI release: https://pypi.org/project/pdm-dockerize/${REVISION}/" >> $GITHUB_STEP_SUMMARY 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | VERSION 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | reports/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # PDM 90 | .pdm.toml 91 | .pdm-python 92 | .pdm-build/ 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | 135 | # Pyre type checker 136 | .pyre/ 137 | -------------------------------------------------------------------------------- /src/pdm_dockerize/commands.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import argparse 4 | import os 5 | from pathlib import Path 6 | 7 | from pdm.cli import actions 8 | from pdm.cli.commands.base import BaseCommand 9 | from pdm.cli.filters import GroupSelection 10 | from pdm.cli.hooks import HookManager 11 | from pdm.cli.options import Option, dry_run_option, groups_group, lockfile_option 12 | from pdm.cli.utils import check_project_file 13 | from pdm.environments import PythonLocalEnvironment 14 | from pdm.project import Project 15 | 16 | from .entrypoint import ProjectEntrypoint 17 | from .installer import DockerizeSynchronizer 18 | 19 | 20 | class DockerizeEnvironment(PythonLocalEnvironment): 21 | """An environment installaing into the dist/docker directory""" 22 | 23 | def __init__( 24 | self, project: Project, *, target: str | None = None, python: str | None = None 25 | ) -> None: 26 | super().__init__(project, python=python) 27 | self.target = Path(target) if target else None 28 | 29 | @property 30 | def packages_path(self) -> Path: 31 | return self.target or self.project.root / "dist/docker" 32 | 33 | 34 | class DockerizeCommand(BaseCommand): 35 | """Generate content for a Docker image""" 36 | 37 | arguments = ( 38 | Option( 39 | "target", 40 | nargs="?", 41 | help="The target into which the docker assets will be generated (default: dist/docker)", 42 | ), 43 | *BaseCommand.arguments, 44 | groups_group, 45 | dry_run_option, 46 | lockfile_option, 47 | ) 48 | 49 | def handle(self, project: Project, options: argparse.Namespace) -> None: 50 | check_project_file(project) 51 | actions.check_lockfile(project) 52 | selection = GroupSelection.from_options(project, options) 53 | hooks = HookManager(project) 54 | env = DockerizeEnvironment(project, target=options.target) 55 | 56 | requirements = [] 57 | selection.validate() 58 | for group in selection: 59 | requirements.extend(project.get_dependencies(group)) 60 | candidates = actions.resolve_candidates_from_lockfile(project, requirements) 61 | synchronizer = DockerizeSynchronizer( 62 | env, 63 | candidates, 64 | dry_run=options.dry_run, 65 | clean=False, 66 | no_editable=True, 67 | reinstall=False, 68 | only_keep=False, 69 | install_self=False, 70 | fail_fast=True, 71 | use_install_cache=False, 72 | ) 73 | synchronizer.synchronize() 74 | 75 | entrypoint = env.packages_path / "entrypoint" 76 | entrypoint.write_text(ProjectEntrypoint(project, hooks).as_script()) 77 | os.chmod(entrypoint, 0o555) 78 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 🚀 0.6.0 (2024-09-23) 4 | 5 | ### 🚨 Breaking changes 6 | 7 | - **pdm**: move to `pdm>=2.19` API 8 | 9 | 10 | ## 🚀 0.5.1 (2024-06-26) 11 | 12 | ### 🐛 Bug fixes 13 | 14 | - **pdm**: `pdm>=2.15` changed `InstallManager` and `Synchronizer` signatures 15 | 16 | ### 📖 Documentation 17 | 18 | - **README**: update plugin install instructions (fix [#8](https://github.com/noirbizarre/pdm-dockerize/issues/8)) 19 | 20 | 21 | ## 🚀 0.5.0 (2024-04-12) 22 | 23 | ### 🚨 Breaking changes 24 | 25 | - **pdm**: support and requires `pdm>=2.14` 26 | 27 | ### 📖 Documentation 28 | 29 | - **CHANGELOG**: fix bad CHANGELOG formatting 30 | - **README**: document the base principle in README 31 | 32 | ### 📦 Build 33 | 34 | - update the build dependencies, update to ruff 0.3+ and lock 35 | 36 | 37 | ## 🚀 0.4.0 (2024-04-04) 38 | 39 | ### 🚨 Breaking changes 40 | 41 | - **pdm**: now depends on `pdm>=2.13` 42 | 43 | ### 💫 New features 44 | 45 | - **env**: allow to source/export some docker-only environment variables or dotenv files 46 | - **shellcheck**: all generated scripts are passing `shellcheck` validation 47 | 48 | ### 📦 Build 49 | 50 | - **deps**: update dev dependencies 51 | 52 | ## 🚀 0.3.1 (2023-12-22) 53 | 54 | ### 🐛 Bug fixes 55 | 56 | - **entrypoint**: run from the app dir and use absolute `$PATH` and `$PYTHONPATH` 57 | 58 | ## 🚀 0.3.0 (2023-12-21) 59 | 60 | ### 💫 New features 61 | 62 | - **PYTHONPATH**: support src-layout and non-root packages for `pdm.backend`-based projects 63 | 64 | ### 📖 Documentation 65 | 66 | - **README**: add some details 67 | 68 | ### 📦 Build 69 | 70 | - update some tooling 71 | 72 | ## 🚀 0.2.4 (2023-12-17) 73 | 74 | ### 🐛 Bug fixes 75 | 76 | - **entrypoint**: remove the `-o pipefail` option which is not cross-platform 77 | 78 | ## 🚀 0.2.3 (2023-12-17) 79 | 80 | ### 🐛 Bug fixes 81 | 82 | - **entrypoint**: ensure entrypoint stop on failures 83 | - **entrypoint**: use POSIX tests to check `env_file` presence 84 | 85 | ## 🚀 0.2.2 (2023-12-17) 86 | 87 | ### 🐛 Bug fixes 88 | 89 | - **entrypoint**: failsafe `env_file` loading with an explicit warning if not loaded 90 | - **entrypoint**: do not fail if envfile is not present 91 | 92 | ## 🚀 0.2.1 (2023-12-17) 93 | 94 | ### 🐛 Bug fixes 95 | 96 | - **entrypoint**: use a `sh`-supported source syntax (eg. '.') 97 | 98 | ## 🚀 0.2.0 (2023-12-17) 99 | 100 | ### 💫 New features 101 | 102 | - **entrypoint**: support parameters passthrough and parameters extrapolation 103 | - python executables needs to be selected using the same filter syntax than scripts 104 | 105 | ### 🐛 Bug fixes 106 | 107 | - **entrypoint**: ensure parameters are given to the resulting command 108 | - **entrypoint**: properly handle whitespace depending on the script kind 109 | - **entrypoint**: support pre and post scripts 110 | - **entrypoint**: take global options into account 111 | - **entrypoint**: use a `sh`-compatible function syntax for `usage` 112 | - **entrypoint**: ensure the entrypoint is executable 113 | - **entrypoint**: export missing environment variables 114 | - **pdm**: support PDM>=2.11 115 | - **pyproject**: add the missing Python 3.12 support classifier 116 | - **python**: minimum Python version is now 3.8.1 to align with Syrupy 117 | 118 | ### 📖 Documentation 119 | 120 | - **README**: Fix the `Dockerfile` snippet 121 | 122 | ## 🚀 0.1.0 (2023-12-14) 123 | 124 | ### 💫 New features 125 | 126 | - Initial import 127 | 128 | ### 📖 Documentation 129 | 130 | - **changelog**: prepare for first release 131 | - **README**: small post-import fixes -------------------------------------------------------------------------------- /src/pdm_dockerize/installer.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import json 4 | from pathlib import Path 5 | from typing import TYPE_CHECKING, Any, Iterable 6 | 7 | from installer.destinations import Scheme 8 | from installer.records import RecordEntry 9 | from pdm.compat import Distribution 10 | from pdm.installers import Synchronizer 11 | from pdm.installers.installers import InstallDestination, WheelFile, install 12 | from pdm.installers.manager import InstallManager 13 | 14 | from . import filters 15 | 16 | if TYPE_CHECKING: 17 | from installer.scripts import ScriptSection 18 | from pdm.environments import BaseEnvironment 19 | from pdm.installers.installers import LinkMethod 20 | from pdm.models.candidates import Candidate 21 | 22 | from .config import DockerizeSettings 23 | 24 | 25 | class DockerizeInstallManager(InstallManager): 26 | """An `InstallManager` filtering installed binaries""" 27 | 28 | def __init__( 29 | self, 30 | environment: BaseEnvironment, 31 | *, 32 | use_install_cache: bool = False, 33 | rename_pth: bool = False, 34 | ) -> None: 35 | super().__init__(environment, use_install_cache=use_install_cache, rename_pth=rename_pth) 36 | settings: DockerizeSettings = self.environment.project.pyproject.settings.get( 37 | "dockerize", {} 38 | ) 39 | self.include = filters.parse(settings, "include_bins") 40 | self.exclude = filters.parse(settings, "exclude_bins") 41 | 42 | def install(self, candidate: Candidate) -> Distribution: 43 | """Install a candidate into the environment, return the distribution""" 44 | prepared = candidate.prepare(self.environment) 45 | additional_metadata = None 46 | if (direct_url := prepared.direct_url()) is not None: 47 | additional_metadata = {"direct_url.json": json.dumps(direct_url, indent=2).encode()} 48 | destination = FilteringDestination( 49 | scheme_dict=self.environment.get_paths(), 50 | interpreter=str(self.environment.interpreter.executable), 51 | script_kind=self.environment.script_kind, 52 | include=self.include, 53 | exclude=self.exclude, 54 | ) 55 | 56 | with WheelFile.open(prepared.build()) as source: 57 | dist_info = install(source, destination, additional_metadata) 58 | return Distribution.at(dist_info) 59 | 60 | 61 | class DockerizeSynchronizer(Synchronizer): 62 | """A `Synchronizer` using the `DockerizeInstallManager`""" 63 | 64 | def get_manager(self, rename_pth: bool = False) -> InstallManager: 65 | return DockerizeInstallManager( 66 | self.environment, 67 | use_install_cache=self.use_install_cache, 68 | rename_pth=rename_pth, 69 | ) 70 | 71 | 72 | class FilteringDestination(InstallDestination): 73 | """An `InstallDestination` filtering installed binaries""" 74 | 75 | def __init__( 76 | self, 77 | *args: Any, 78 | link_method: LinkMethod = "copy", 79 | include: list[str] | None = None, 80 | exclude: list[str] | None = None, 81 | **kwargs: Any, 82 | ) -> None: 83 | super().__init__( 84 | *args, 85 | link_method=link_method, 86 | **kwargs, 87 | ) 88 | 89 | self.include = include or [] 90 | self.exclude = exclude or [] 91 | 92 | def write_script( 93 | self, name: str, module: str, attr: str, section: ScriptSection 94 | ) -> RecordEntry: 95 | if filters.match(name, self.include) and not filters.match(name, self.exclude): 96 | return super().write_script(name, module, attr, section) 97 | return RecordEntry("", None, None) 98 | 99 | def finalize_installation( 100 | self, 101 | scheme: Scheme, 102 | record_file_path: str | Path, 103 | records: Iterable[tuple[Scheme, RecordEntry]], 104 | ) -> None: 105 | records = [(scheme, record) for scheme, record in records if record.path] 106 | return super().finalize_installation(scheme, record_file_path, records) 107 | -------------------------------------------------------------------------------- /tests/test_pdm_dockerize.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | from dataclasses import dataclass, field 5 | from pathlib import Path 6 | from typing import TYPE_CHECKING 7 | 8 | import pytest 9 | 10 | if TYPE_CHECKING: 11 | from pdm.project import Project 12 | from pdm.pytest import PDMCallable 13 | from syrupy import SnapshotAssertion 14 | 15 | 16 | @pytest.fixture 17 | def project(project: Project) -> Project: 18 | project.pyproject.settings["dockerize"] = {"include": "*"} 19 | project.pyproject.metadata["requires-python"] = ">=3.8" 20 | project.pyproject.metadata["dependencies"] = ["Faker"] 21 | project.pyproject.settings["scripts"] = {"test": "pytest"} 22 | return project 23 | 24 | 25 | def test_expose_version(): 26 | import pdm_dockerize 27 | 28 | assert pdm_dockerize.__version__ 29 | 30 | 31 | def test_generate_docker_dist(project: Project, pdm: PDMCallable, snapshot: SnapshotAssertion): 32 | project.pyproject.settings["dockerize"]["include_bins"] = "*" 33 | project.pyproject.write() 34 | pdm("lock", obj=project, strict=True) 35 | 36 | pdm("dockerize -v", obj=project, strict=True) 37 | 38 | dist = project.root / "dist/docker" 39 | assert dist.is_dir() 40 | 41 | entrypoint = dist / "entrypoint" 42 | assert entrypoint.exists() 43 | assert os.access(entrypoint, os.X_OK) 44 | assert entrypoint.read_text() == snapshot 45 | 46 | lib = dist / "lib" 47 | assert lib.is_dir() 48 | assert (lib / "faker").is_dir() 49 | assert not (lib / "faker").is_symlink() 50 | 51 | bin = dist / "bin" 52 | assert bin.is_dir() 53 | assert (bin / "faker").is_file() 54 | 55 | 56 | def test_generate_docker_dist_to_target( 57 | project: Project, pdm: PDMCallable, snapshot: SnapshotAssertion, tmp_path: Path 58 | ): 59 | project.pyproject.settings["dockerize"]["include_bins"] = "*" 60 | project.pyproject.write() 61 | pdm("lock", obj=project, strict=True) 62 | 63 | target = tmp_path / "target" 64 | 65 | pdm(f"dockerize -v {target}", obj=project, strict=True) 66 | 67 | dist = project.root / "dist/docker" 68 | assert not dist.exists() 69 | 70 | entrypoint = target / "entrypoint" 71 | assert entrypoint.exists() 72 | assert os.access(entrypoint, os.X_OK) 73 | assert entrypoint.read_text() == snapshot 74 | 75 | lib = target / "lib" 76 | assert lib.is_dir() 77 | assert (lib / "faker").is_dir() 78 | assert not (lib / "faker").is_symlink() 79 | 80 | bin = target / "bin" 81 | assert bin.is_dir() 82 | assert (bin / "faker").is_file() 83 | 84 | 85 | @dataclass 86 | class BinFilterCase: 87 | id: str 88 | include: str | list[str] | None = None 89 | exclude: str | list[str] | None = None 90 | expected: list[str] = field(default_factory=list) 91 | 92 | 93 | BIN_FILTER_CASES = ( 94 | BinFilterCase("no-filter"), 95 | BinFilterCase( 96 | "include-all", include="*", expected=["black", "blackd", "faker", "pytest", "py.test"] 97 | ), 98 | BinFilterCase("include-list", include=["faker", "black"], expected=["faker", "black"]), 99 | BinFilterCase("exclude-all", include="*", exclude="*"), 100 | BinFilterCase( 101 | "exclude-list", 102 | include="*", 103 | exclude=["any", "blackd"], 104 | expected=["black", "pytest", "py.test", "faker"], 105 | ), 106 | BinFilterCase( 107 | "include-all-but-prefix", include="*", exclude="py*", expected=["black", "blackd", "faker"] 108 | ), 109 | BinFilterCase( 110 | "include-prefix-except", 111 | include="py*", 112 | exclude="py", 113 | expected=["pytest", "py.test"], 114 | ), 115 | ) 116 | 117 | 118 | @pytest.mark.parametrize("case", [pytest.param(case, id=case.id) for case in BIN_FILTER_CASES]) 119 | def test_binaries_filtering(project: Project, pdm: PDMCallable, case: BinFilterCase): 120 | project.pyproject.metadata["dependencies"] = ["Faker", "pytest", "black"] 121 | if case.include: 122 | project.pyproject.settings["dockerize"]["include_bins"] = case.include 123 | if case.exclude: 124 | project.pyproject.settings["dockerize"]["exclude_bins"] = case.exclude 125 | project.pyproject.write() 126 | 127 | pdm("lock", obj=project, strict=True) 128 | pdm("dockerize -v", obj=project, strict=True) 129 | 130 | bindir = project.root / "dist/docker/bin" 131 | if bindir.exists(): 132 | bins = [bin.name for bin in bindir.iterdir() if bin.is_file() and os.access(bin, os.X_OK)] 133 | else: 134 | bins = [] 135 | 136 | assert set(bins) == set(case.expected) 137 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "pdm-dockerize" 3 | description = "Help generating docker images from PDM projects" 4 | authors = [ 5 | {name = "Axel Haustant", email = "noirbizarre@gmail.com"}, 6 | ] 7 | readme = "README.md" 8 | license = {text = "MIT"} 9 | requires-python = ">=3.8.1" 10 | keywords = [] 11 | dynamic = ["version"] 12 | classifiers = [ 13 | "Development Status :: 4 - Beta", 14 | "Environment :: Plugins", 15 | "Intended Audience :: Developers", 16 | "Programming Language :: Python", 17 | "Programming Language :: Python :: 3", 18 | "Programming Language :: Python :: 3 :: Only", 19 | "Programming Language :: Python :: 3.8", 20 | "Programming Language :: Python :: 3.9", 21 | "Programming Language :: Python :: 3.10", 22 | "Programming Language :: Python :: 3.11", 23 | "Programming Language :: Python :: 3.12", 24 | "Topic :: Software Development", 25 | "Topic :: Software Development :: Build Tools", 26 | "Topic :: Software Development :: Code Generators", 27 | "Typing :: Typed", 28 | ] 29 | 30 | dependencies = [ 31 | "pdm>=2.19", 32 | ] 33 | [project.urls] 34 | Homepage = "https://github.com/noirbizarre/pdm-dockerize" 35 | Documentation = "https://github.com/noirbizarre/pdm-dockerize#readme" 36 | Repository = "https://github.com/noirbizarre/pdm-dockerize" 37 | Issues = "https://github.com/noirbizarre/pdm-dockerize/issues" 38 | 39 | [project.entry-points.pdm] 40 | dockerize = "pdm_dockerize:plugin" 41 | 42 | [build-system] 43 | requires = ["pdm-backend"] 44 | build-backend = "pdm.backend" 45 | 46 | [tool.pdm] 47 | plugins = [ 48 | "sync-pre-commit-lock", 49 | ] 50 | 51 | [tool.pdm.version] 52 | source = "scm" 53 | write_to = "pdm_dockerize/VERSION" 54 | 55 | 56 | [tool.pdm.dev-dependencies] 57 | test = [ 58 | "pdm[pytest]", 59 | "pytest>=7.1.2", 60 | "pytest-sugar>=0.9.5", 61 | "pytest-cov>=3.0.0", 62 | "syrupy>=3.0.6", 63 | "shellcheck-py>=0.9.0.6", 64 | ] 65 | lint = [ 66 | "codespell>=2.2.6", 67 | "mypy>=1.5.1", 68 | "ruff>=0.3.0", 69 | "tomli; python_version<'3.11'", 70 | ] 71 | tox = [ 72 | "tox", 73 | "tox-pdm>=0.5", 74 | ] 75 | release = [ 76 | "emotional", 77 | ] 78 | 79 | 80 | [tool.pdm.scripts] 81 | test.help = "Run the test suite" 82 | test.cmd = "pytest" 83 | 84 | lint.help = "Lint all tracked files using pre-commit" 85 | lint.cmd = "pre-commit run --all-files" 86 | 87 | format.help = "Format the code according to known rules" 88 | format.composite = [ 89 | "codespell --write-changes --interactive 2", 90 | # See: https://docs.astral.sh/ruff/formatter/#sorting-imports 91 | "ruff check --select I --fix-only --show-fixes src tests", 92 | "ruff format src tests" 93 | ] 94 | 95 | typing.help = "Full typing linting (includes imported packages and uncommmited files)" 96 | typing.cmd = "mypy src tests --warn-unused-ignores" 97 | 98 | cover.help = "Run the test suite with coverage" 99 | pre_cover = "coverage erase" 100 | cover.composite = [ 101 | """ 102 | test 103 | --cov 104 | --cov-report=term 105 | --cov-report=html:reports/coverage 106 | --cov-report=xml:reports/coverage.xml 107 | --no-cov-on-fail 108 | --junitxml=reports/tests.xml 109 | """ 110 | ] 111 | 112 | "cover:all".help = "Run the test suite against all supported Python version" 113 | "pre_cover:all" = "coverage erase" 114 | "cover:all".cmd = "tox --parallel" 115 | 116 | changelog.help = "Update the changelog" 117 | changelog.cmd = "cz changelog --incremental" 118 | 119 | 120 | [tool.commitizen] 121 | name = "emotional" 122 | github = "noirbizarre/pdm-dockerize" 123 | order_by_scope = true 124 | version_provider = "scm" 125 | major_version_zero = true # Remove this line for the first 1.x release 126 | 127 | 128 | [tool.pdm.ide] 129 | linters = ["ruff"] 130 | formatter = "black" 131 | testing = "pytest" 132 | 133 | 134 | [tool.pdm.dockerize] 135 | include = "*" 136 | 137 | 138 | [tool.pytest.ini_options] 139 | addopts = "-ra --log-disable unearth.evaluator --log-disable unearth.collector --log-disable unearth.auth --log-disable pdm.termui" 140 | norecursedirs = ".git build dist" 141 | testpaths = [ 142 | "src/", 143 | "tests/", 144 | ] 145 | 146 | 147 | [tool.coverage.run] 148 | source = ["src"] 149 | branch = true 150 | omit = [ 151 | "tests/*", 152 | ] 153 | 154 | [tool.coverage.report] 155 | exclude_also = [ 156 | 'def __repr__', 157 | 'if TYPE_CHECKING:', 158 | ] 159 | ignore_errors = true 160 | 161 | 162 | [tool.ruff] 163 | line-length = 100 164 | respect-gitignore = true 165 | src = ["src", "tests"] 166 | 167 | [tool.ruff.lint] 168 | select = [ 169 | # Pyflakes 170 | "F", 171 | # Pycodestyle 172 | "E", 173 | "W", 174 | # McCabe # fake8-comprehension 175 | "C", 176 | # Pyupgrade 177 | "UP", 178 | # isort 179 | "I001" 180 | ] 181 | 182 | [tool.ruff.lint.isort] 183 | known-first-party = ["pdm_dockerize"] 184 | known-third-party = ["pytest", "syrupy"] 185 | 186 | 187 | [tool.mypy] 188 | exclude = [ 189 | "docs", 190 | "build", 191 | "dist", 192 | ] 193 | 194 | 195 | [tool.codespell] 196 | skip = '__snapshots__,./reports' 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pdm-dockerize 2 | 3 | [![CI](https://github.com/noirbizarre/pdm-dockerize/actions/workflows/ci.yml/badge.svg)](https://github.com/noirbizarre/pdm-dockerize/actions/workflows/ci.yml) 4 | [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/noirbizarre/pdm-dockerize/main.svg)](https://results.pre-commit.ci/latest/github/noirbizarre/pdm-dockerize/main) 5 | [![PyPI](https://img.shields.io/pypi/v/pdm-dockerize)](https://pypi.org/project/pdm-dockerize/) 6 | [![PyPI - License](https://img.shields.io/pypi/l/pdm-dockerize)](https://pypi.org/project/pdm-dockerize/) 7 | 8 | Help generating docker image from PDM projects. 9 | 10 | ## Installation 11 | 12 | Install `pdm-dockerize`: 13 | 14 | ### With `pipx` 15 | 16 | If you installed `pdm` with `pipx` and want to have the command for all projects: 17 | 18 | ```console 19 | pipx inject pdm pdm-dockerize 20 | ``` 21 | 22 | ### With `pip` 23 | 24 | If you manually installed `pdm` with `pip`, just install the extra dependency in the same environment: 25 | 26 | ```console 27 | pip install pdm-dockerize 28 | ``` 29 | 30 | ### With `pdm` 31 | 32 | You can also install it as a standard `pdm` plugin. 33 | 34 | Either globally: 35 | 36 | ```console 37 | pdm self add pdm-dockerize 38 | ``` 39 | 40 | Either as a local plugin in your project: 41 | 42 | ```toml 43 | [tool.pdm] 44 | plugins = [ 45 | "pdm-dockerize", 46 | ] 47 | ``` 48 | 49 | Then: 50 | 51 | ```coonsole 52 | pdm install --plugins 53 | ``` 54 | 55 | ## Usage 56 | 57 | Just use `pdm dockerize` in your multistage build: 58 | 59 | ```dockerfile 60 | # syntax=docker/dockerfile:1 61 | ARG PY_VERSION=3.11 62 | 63 | ## 64 | # Build stage: build and install dependencies 65 | ## 66 | FROM python:${PY_VERSION} AS builder 67 | 68 | ARG VERSION=0.dev 69 | ENV PDM_BUILD_SCM_VERSION=${VERSION} 70 | 71 | WORKDIR /project 72 | 73 | # install PDM 74 | RUN pip install -U pip setuptools wheel 75 | RUN pip install pdm pdm-dockerize 76 | 77 | RUN --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ 78 | --mount=type=bind,source=pdm.lock,target=pdm.lock \ 79 | --mount=type=cache,target=$HOME/.cache,uid=$UUID \ 80 | pdm dockerize --prod -v 81 | 82 | ## 83 | # Run stage: create the final runtime container 84 | ## 85 | FROM python:${PY_VERSION} AS runtime 86 | 87 | WORKDIR /app 88 | 89 | # Fetch built dependencies 90 | COPY --from=builder /project/dist/docker /app 91 | # Copy needed files from your project (filter using `.dockerignore`) 92 | COPY . /app 93 | 94 | ENTRYPOINT ["/app/entrypoint"] 95 | CMD ["your-default-command"] 96 | ``` 97 | 98 | ### Selecting scripts 99 | 100 | By default, the `dockerize` command will render a script without any command as it does not select any script by default. 101 | 102 | You can select scripts with the `include` and `exclude` properties of the `tool.pdm.dockerize` section. 103 | Those properties are optional, can be either a string or list of string. 104 | Each string is a [`fnmatch` filter pattern](https://docs.python.org/3/library/fnmatch.html) 105 | 106 | Dockerize first select script based on the include patterns and then filter-out those matching with any exclude pattern. 107 | 108 | #### Include all scripts 109 | 110 | ```toml 111 | [tool.pdm.dockerize] 112 | include = "*" 113 | ``` 114 | 115 | #### Include some specific scripts 116 | 117 | ```toml 118 | [tool.pdm.dockerize] 119 | include = ["my-script", "my-other-script"] 120 | ``` 121 | 122 | #### Include all scripts excluding those matching `prefix-*` 123 | 124 | ```toml 125 | [tool.pdm.dockerize] 126 | include = "*" 127 | exclude = "prefix-*" 128 | ``` 129 | 130 | #### Include all scripts matching a prefix but two 131 | 132 | ```toml 133 | [tool.pdm.dockerize] 134 | include = "prefix-*" 135 | exclude = ["prefix-not-you", "prefix-you-neither"] 136 | ``` 137 | 138 | ### Selecting binaries 139 | 140 | By default, the `dockerize` command will not copy any python executable provided by your dependencies. 141 | You can select binaries with the `include_bins` and `exclude_bins` properties of the `tool.pdm.dockerize` section. 142 | Syntax and behavior are exactly the exact sames than `include`/`exclude` for script selection. 143 | 144 | #### Include all python executables 145 | 146 | ```toml 147 | [tool.pdm.dockerize] 148 | include_bins = "*" 149 | ``` 150 | 151 | #### Include some specific executables 152 | 153 | Most of the time, you will look like this 154 | 155 | ```toml 156 | [tool.pdm.dockerize] 157 | include = ["uvicorn"] 158 | ``` 159 | 160 | ### Controlling environment 161 | 162 | `pdm-dockerize` respects defined environment variables: 163 | - scripts `env` variables are properly set 164 | - shared `_.env` variables are properly set 165 | - scripts `env_file` are properly loaded 166 | - shared `_.env_file` are properly loaded 167 | 168 | In addition, you can define some docker-only environment variables using the `tool.pdm.dockerize.env` table 169 | or some docker-only `.env` files using `tool.pdm.dockerize.env_file` 170 | 171 | #### Defining docker-only environment variables 172 | 173 | Those environment variables will only be effective in the docker entrypoint. 174 | 175 | ```toml 176 | [tool.pdm.dockerize.env] 177 | VAR = "value" 178 | ``` 179 | 180 | #### Loading docker-only environment files 181 | 182 | This file will only be loaded in the docker entrypoint. 183 | 184 | ```toml 185 | [tool.pdm.dockerize] 186 | env_file = "docker.env" 187 | ``` 188 | 189 | ## Internals 190 | 191 | This plugin works by providing by subclassing some `pdm.installers` classes to reuse the installation process: 192 | - `DockerizeInstallManager`, a `pdm` `InstallManager` filtering binaries 193 | - `DockerizeSynchronizer`, a `pdm` `Synchronizer` using a `DockerizeInstallManager` as `InstallManager` 194 | - `FilteringDestination`, a `pdm` `InstallDestination` filtering binaries 195 | 196 | This way, the dockerization is using the same installation process just tuned for docker and augmented with `pdm-dockerize` specifics. 197 | 198 | ## Contributing 199 | 200 | Read the [dedicated contributing guidelines](./CONTRIBUTING.md). 201 | -------------------------------------------------------------------------------- /tests/test_entrypoint.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from dataclasses import dataclass 4 | from textwrap import dedent 5 | from typing import TYPE_CHECKING, Any 6 | 7 | import pytest 8 | from pdm.cli.hooks import HookManager 9 | 10 | from pdm_dockerize.entrypoint import ProjectEntrypoint 11 | 12 | if TYPE_CHECKING: 13 | from pdm.project import Project 14 | from syrupy import SnapshotAssertion 15 | 16 | from tests.conftest import ShellcheckFixture 17 | 18 | 19 | def entrypoint_for(project: Project, hooks: HookManager | None = None) -> str: 20 | return str(ProjectEntrypoint(project, hooks or HookManager(project))) 21 | 22 | 23 | CASES = { 24 | "no_script": {}, 25 | "cmd-as-str": {"test": "pytest"}, 26 | "cmd-as-dict": {"test": {"cmd": "pytest"}}, 27 | "cmd-as-list": {"test": {"cmd": ["pytest", "--with", "--params"]}}, 28 | "shell": {"test": {"shell": "pytest"}}, 29 | "call": {"test": {"call": "my.app:main"}}, 30 | "call-with-arguments": {"test": {"call": "my.app:main('dev', key='value')"}}, 31 | "composite": {"test": {"composite": ["first", "second"]}}, 32 | "composite-inline": { 33 | "_helper": "should be inlined", 34 | "command": {"composite": ["_helper something"]}, 35 | }, 36 | "env": {"test": {"cmd": "pytest", "env": {"WHATEVER": "42", "OTHER": "value"}}}, 37 | "shared-env": { 38 | "_": {"env": {"WHATEVER": "42", "OTHER": "value"}}, 39 | "test": {"cmd": "pytest", "env": {"OTHER": "new-value"}}, 40 | }, 41 | "env_file": {"test": {"cmd": "pytest", "env_file": ".env"}}, 42 | "shared-env_file": { 43 | "_": {"env_file": ".env"}, 44 | "test": {"cmd": "pytest"}, 45 | }, 46 | "env_file-override": {"test": {"cmd": "pytest", "env_file": {"override": ".env"}}}, 47 | "env_file-precedance": { 48 | "test": {"cmd": "pytest", "env": {"WHATEVER": "42"}, "env_file": ".env"} 49 | }, 50 | "env_file-override-precedance": { 51 | "test": {"cmd": "pytest", "env": {"WHATEVER": "42"}, "env_file": {"override": ".env"}} 52 | }, 53 | "pre-post": { 54 | "pre_test": "pre", 55 | "test": "pytest", 56 | "post_test": "post", 57 | }, 58 | "whitespaces": { 59 | "cmd": dedent( 60 | """\ 61 | whitespaces 62 | are 63 | ignored 64 | """ 65 | ), 66 | "shell": { 67 | "shell": dedent( 68 | """\ 69 | whitespaces 70 | should be 71 | preserved 72 | """ 73 | ) 74 | }, 75 | "composite": { 76 | "composite": [ 77 | dedent( 78 | """\ 79 | whitespaces 80 | are 81 | ignored 82 | """ 83 | ) 84 | ] 85 | }, 86 | }, 87 | "args-placeholder": { 88 | "cmd": "cmd.before {args} cmd.after", 89 | "shell": {"shell": "shell.before {args} shell.after"}, 90 | "composite": {"composite": ["cmd --something", "shell {args}"]}, 91 | }, 92 | "args-placeholder-with-defaults": { 93 | "cmd": "cmd.before {args:default value} cmd.after", 94 | "shell": {"shell": "shell.before {args:default value} shell.after"}, 95 | "composite": {"composite": ["cmd --something", "shell {args:default value}"]}, 96 | }, 97 | } 98 | 99 | 100 | @pytest.mark.parametrize("scripts", [pytest.param(scripts, id=id) for id, scripts in CASES.items()]) 101 | def test_serialization( 102 | project: Project, 103 | snapshot: SnapshotAssertion, 104 | scripts: dict[str, Any], 105 | shellcheck: ShellcheckFixture, 106 | ): 107 | project.pyproject.settings["dockerize"] = {"include": "*"} 108 | project.pyproject.settings["scripts"] = scripts 109 | entrypoint = entrypoint_for(project) 110 | assert entrypoint == snapshot 111 | shellcheck(entrypoint) 112 | 113 | 114 | @dataclass 115 | class FilterCase: 116 | id: str 117 | include: str | list[str] | None = None 118 | exclude: str | list[str] | None = None 119 | 120 | 121 | FILTER_CASES = ( 122 | FilterCase("no-filter"), 123 | FilterCase("include-all", include="*"), 124 | FilterCase("include-list", include=["test", "ns:task1"]), 125 | FilterCase("exclude-all", include="*", exclude="*"), 126 | FilterCase("exclude-list", include="*", exclude=["test", "ns:task1"]), 127 | FilterCase("include-all-but-prefix", include="*", exclude="ns:*"), 128 | FilterCase("include-prefix-except", include="ns:*", exclude="ns:task1"), 129 | ) 130 | 131 | 132 | @pytest.mark.parametrize("case", [pytest.param(case, id=case.id) for case in FILTER_CASES]) 133 | def test_filtering(project: Project, snapshot: SnapshotAssertion, case: FilterCase): 134 | project.pyproject.settings["scripts"] = { 135 | "test": "pytest", 136 | "test:something": "pytest something", 137 | "ns:task1": "ns:task1", 138 | "ns:task2": "ns:task2", 139 | "ns:task3": "ns:task3", 140 | } 141 | dockerize = {} 142 | if case.include is not None: 143 | dockerize["include"] = case.include 144 | if case.exclude is not None: 145 | dockerize["exclude"] = case.exclude 146 | project.pyproject.settings["dockerize"] = dockerize 147 | assert entrypoint_for(project) == snapshot 148 | 149 | 150 | def test_pythonpath_explicit_src_layout(project: Project, snapshot: SnapshotAssertion): 151 | project.pyproject._data["build-system"] = { 152 | "requires": ["pdm-backend"], 153 | "build-backend": "pdm.backend", 154 | } 155 | project.pyproject.settings["build"] = {"package-dir": "src"} 156 | assert entrypoint_for(project) == snapshot 157 | 158 | 159 | def test_pythonpath_implicit_src_layout(project: Project, snapshot: SnapshotAssertion): 160 | project.pyproject._data["build-system"] = { 161 | "requires": ["pdm-backend"], 162 | "build-backend": "pdm.backend", 163 | } 164 | pkg_dir = project.root / "src/pkg" 165 | pkg_dir.mkdir(parents=True, exist_ok=True) 166 | pkg_init = pkg_dir / "__init__.py" 167 | pkg_init.write_text("") 168 | assert entrypoint_for(project) == snapshot 169 | 170 | 171 | def test_global_env( 172 | project: Project, 173 | snapshot: SnapshotAssertion, 174 | shellcheck: ShellcheckFixture, 175 | ): 176 | project.pyproject.settings["dockerize"] = {"include": "*", "env": {"VAR": 42, "LAST": "value"}} 177 | project.pyproject.settings["scripts"] = {"hello": "echo 'Hello'"} 178 | entrypoint = entrypoint_for(project) 179 | assert entrypoint == snapshot 180 | shellcheck(entrypoint) 181 | 182 | 183 | def test_global_env_file( 184 | project: Project, 185 | snapshot: SnapshotAssertion, 186 | shellcheck: ShellcheckFixture, 187 | ): 188 | project.pyproject.settings["dockerize"] = {"include": "*", "env_file": "docker.env"} 189 | project.pyproject.settings["scripts"] = {"hello": "echo 'Hello'"} 190 | entrypoint = entrypoint_for(project) 191 | assert entrypoint == snapshot 192 | shellcheck(entrypoint) 193 | -------------------------------------------------------------------------------- /src/pdm_dockerize/entrypoint.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import io 4 | import re 5 | import shlex 6 | from dataclasses import dataclass 7 | from functools import cached_property 8 | from typing import TYPE_CHECKING 9 | 10 | from pdm.cli.commands.run import RE_ARGS_PLACEHOLDER, TaskRunner, exec_opts 11 | 12 | from . import filters 13 | 14 | if TYPE_CHECKING: 15 | from pdm.cli.commands.run import Task 16 | from pdm.cli.hooks import HookManager 17 | from pdm.project import Project 18 | 19 | from .config import DockerizeSettings 20 | 21 | 22 | INDENT = 4 * " " 23 | RE_CALL = re.compile(r"(?P[\w\d_.]+):(?P[\w\d_]+)(?:\((?P.*?)\))?") 24 | 25 | 26 | @dataclass 27 | class ProjectEntrypoint: 28 | project: Project 29 | hooks: HookManager 30 | 31 | @cached_property 32 | def settings(self) -> DockerizeSettings: 33 | return self.project.pyproject.settings.get("dockerize", {}) 34 | 35 | @cached_property 36 | def runner(self) -> TaskRunner: 37 | return TaskRunner(self.project, hooks=self.hooks) 38 | 39 | def select_scripts(self) -> list[str]: 40 | """ 41 | List all scripts eligible to docker entrypoint according filtering 42 | """ 43 | include = filters.parse(self.settings, "include") 44 | exclude = filters.parse(self.settings, "exclude") 45 | scripts = self.project.pyproject.settings.get("scripts", {}).keys() 46 | scripts = [script for script in scripts if not script.startswith("_")] 47 | included = [script for script in scripts if filters.match(script, include)] 48 | return [script for script in included if not filters.match(script, exclude)] 49 | 50 | def __str__(self) -> str: 51 | return self.as_script() 52 | 53 | def as_script(self) -> str: 54 | """Render the `sh` entrypoint""" 55 | out = io.StringIO() 56 | 57 | out.write("#!/usr/bin/env sh\n\n") 58 | out.write("set -eu\n\n") 59 | out.write('dirname=$(dirname "$0")\n') 60 | out.write('cmd=${1:-""}\n') 61 | out.write('[ "$cmd" ] && shift\n') 62 | out.write('cd "$dirname" > /dev/null\n') 63 | out.write("\n") 64 | out.write(self.export_env()) 65 | out.write("\n") 66 | out.write(self.usage()) 67 | out.write("\n") 68 | out.write("case $cmd in\n") 69 | 70 | for script in self.select_scripts(): 71 | out.write(self.case(script)) 72 | 73 | out.write(f"{INDENT}*)\n") 74 | out.write(f"{2 * INDENT}usage\n") 75 | out.write(f"{2 * INDENT};;\n") 76 | out.write("esac\n") 77 | 78 | return out.getvalue() 79 | 80 | def export_env(self) -> str: 81 | """Export the environment variables""" 82 | out = io.StringIO() 83 | path = ["$(pwd)/bin", "$PATH"] 84 | pythonpath = ["$(pwd)/lib"] 85 | if package_dir := self.get_package_dir(): 86 | pythonpath.insert(0, package_dir) 87 | out.write(self.export_var("PYTHONPATH", ":".join(f'"{p}"' for p in pythonpath))) 88 | out.write(self.export_var("PATH", ":".join(f'"{p}"' for p in path))) 89 | if env := self.settings.get("env"): 90 | for name, value in env.items(): 91 | out.write(self.export_var(name, str(value))) 92 | if env_file := self.settings.get("env_file"): 93 | out.write(self.source_env(env_file)) 94 | return out.getvalue() 95 | 96 | def export_var(self, name: str, value: str, indent: int = 0) -> str: 97 | prefix = INDENT * indent 98 | out = io.StringIO() 99 | if not value.startswith('"') or not value.endswith('"'): 100 | value = f'"{value}"' 101 | out.write(f"{prefix}{name}={value}\n") 102 | out.write(f"{prefix}export {name}\n") 103 | return out.getvalue() 104 | 105 | def get_package_dir(self) -> str | None: 106 | """An optional directory containing the project sources""" 107 | # TODO: find a better way to identify package-dir 108 | build_system = self.project.backend.build_system() 109 | if not build_system.get("build-backend") == "pdm.backend": 110 | return None 111 | default = "src" if self.project.root.joinpath("src").exists() else None 112 | pkgdir = self.project.pyproject.settings.get("build", {}).get("package-dir", default) 113 | return f"$(pwd)/{pkgdir}" if pkgdir else None 114 | 115 | def usage(self) -> str: 116 | """Render the entrypoint usage/help""" 117 | out = io.StringIO() 118 | out.write("usage() {\n") 119 | out.write(f'{INDENT}echo "Available commands"\n') 120 | out.write(f'{INDENT}echo "=================="\n') 121 | 122 | for script in self.select_scripts(): 123 | task = self.runner.get_task(script) 124 | if task is None: 125 | continue 126 | if task.kind == "cmd" and isinstance(task.args, list): 127 | description = " ".join(task.args) 128 | else: 129 | description = task.short_description 130 | if "\n" in description: 131 | description = f"{description.splitlines()[0]}…" 132 | out.write(f'{INDENT}echo "{script}: {description}"\n') 133 | out.write("}\n") 134 | return out.getvalue() 135 | 136 | def case(self, script: str) -> str: 137 | """Render a script case for a given task""" 138 | task = self.runner.get_task(script) 139 | out = io.StringIO() 140 | out.write(f"{INDENT}{task.name})\n") 141 | 142 | if pre := self.runner.get_task(f"pre_{script}"): 143 | out.write(self.script_for(pre)) 144 | 145 | out.write(self.script_for(task)) 146 | 147 | if post := self.runner.get_task(f"post_{script}"): 148 | out.write(self.script_for(post)) 149 | 150 | out.write(f"{2 * INDENT};;\n") 151 | return out.getvalue() 152 | 153 | def script_for(self, task: Task, params: str | None = None) -> str: 154 | """Render the script part for a single task""" 155 | out = io.StringIO() 156 | opts = exec_opts(self.runner.global_options, task.options) 157 | if (envfile := opts.get("env_file")) and isinstance(envfile, str): 158 | out.write(self.source_env(envfile, indent=2)) 159 | 160 | for var, value in (opts.get("env") or {}).items(): 161 | out.write(self.export_var(var, value, indent=2)) 162 | 163 | if isinstance(envfile, dict) and (override := envfile.get("override")): 164 | out.write(self.source_env(override, indent=2)) 165 | 166 | if task.kind == "call": 167 | out.write(self.call_script(task)) 168 | elif task.kind == "cmd": 169 | out.write(self.cmd_script(task, params)) 170 | elif task.kind == "composite": 171 | out.write(self.composite_script(task, params)) 172 | else: 173 | out.write(self.shell_script(task, params)) 174 | return out.getvalue() 175 | 176 | def source_env(self, envfile: str, indent: int = 0) -> str: 177 | out = io.StringIO() 178 | prefix = indent * INDENT 179 | out.write(f"{prefix}set -o allexport\n") 180 | out.write(f"{prefix}# shellcheck source=/dev/null\n") 181 | out.write(f"{prefix}[ -f {envfile} ] && . {envfile} ") 182 | out.write(f"|| echo '{envfile} is ignored as it does not exist.'\n") 183 | out.write(f"{prefix}set +o allexport\n") 184 | return out.getvalue() 185 | 186 | def cmd_script(self, task: Task, params: str | None = None) -> str: 187 | if isinstance(task.args, str): 188 | script, interpolated = self.interpolate(task.args) 189 | script = " ".join(shlex.split(script, posix=False)) 190 | else: 191 | script, interpolated = self.interpolate(shlex.join(task.args)) 192 | if not (params or interpolated): 193 | params = '"$@"' 194 | if params: 195 | script += f" {params}" 196 | return f"{2 * INDENT}{script}\n" 197 | 198 | def call_script(self, task: Task) -> str: 199 | if not (m := RE_CALL.match(task.args)): 200 | raise ValueError("Unparsable call task {tasks.name}: {tasks.args}") 201 | pkg = m.group("pkg") 202 | fn = m.group("fn") 203 | args = m.group("args") or "" 204 | return f'{2 * INDENT}python -c "from {pkg} import {fn}; {fn}({args})"\n' 205 | 206 | def shell_script(self, task: Task, params: str | None = None) -> str: 207 | out = io.StringIO() 208 | args, interpolated = self.interpolate(task.args) 209 | lines = args.splitlines() 210 | for idx, line in enumerate(lines, 1): 211 | out.write(f"{2 * INDENT}{line}") 212 | if idx == len(lines): 213 | if params: 214 | out.write(f" {params}") 215 | if not interpolated: 216 | out.write(' "$@"') 217 | out.write("\n") 218 | return out.getvalue() 219 | 220 | def composite_script(self, task: Task, params: str | None = None) -> str: 221 | out = io.StringIO() 222 | cmds, interpolated = zip(*(self.interpolate(cmd) for cmd in task.args)) 223 | if not params and not any(interpolated): 224 | params = '"$@"' 225 | for cmd in cmds: 226 | args = shlex.split(cmd, posix=False) 227 | if inline := self.runner.get_task(args[0]): 228 | args = args[1:] 229 | script = " ".join(args) 230 | if params: 231 | script += f" {params}" 232 | out.write(self.script_for(inline, script)) 233 | else: 234 | out.write(f"{2 * INDENT}{' '.join(args)} {params or ''}\n") 235 | return out.getvalue() 236 | 237 | def interpolate(self, script: str) -> tuple[str, bool]: 238 | """Interpolate the `{args:[defaults]} placeholder in a string""" 239 | 240 | def replace(m: re.Match[str]) -> str: 241 | if default := m.group("default"): 242 | return f'"${{@:-{default}}}"' 243 | return '"$@"' 244 | 245 | interpolated, count = RE_ARGS_PLACEHOLDER.subn(replace, script) 246 | return interpolated, count > 0 247 | -------------------------------------------------------------------------------- /pdm.lock: -------------------------------------------------------------------------------- 1 | # This file is @generated by PDM. 2 | # It is not intended for manual editing. 3 | 4 | [metadata] 5 | groups = ["default", "lint", "release", "test", "tox"] 6 | strategy = ["inherit_metadata"] 7 | lock_version = "4.5.0" 8 | content_hash = "sha256:41ada2795a606f8553e95c817f21bc3ee18270fd711b08aefecfc02a1c535fcf" 9 | 10 | [[metadata.targets]] 11 | requires_python = ">=3.8.1" 12 | 13 | [[package]] 14 | name = "anyio" 15 | version = "4.5.0" 16 | requires_python = ">=3.8" 17 | summary = "High level compatibility layer for multiple asynchronous event loop implementations" 18 | groups = ["default", "test"] 19 | dependencies = [ 20 | "exceptiongroup>=1.0.2; python_version < \"3.11\"", 21 | "idna>=2.8", 22 | "sniffio>=1.1", 23 | "typing-extensions>=4.1; python_version < \"3.11\"", 24 | ] 25 | files = [ 26 | {file = "anyio-4.5.0-py3-none-any.whl", hash = "sha256:fdeb095b7cc5a5563175eedd926ec4ae55413bb4be5770c424af0ba46ccb4a78"}, 27 | {file = "anyio-4.5.0.tar.gz", hash = "sha256:c5a275fe5ca0afd788001f58fca1e69e29ce706d746e317d660e21f70c530ef9"}, 28 | ] 29 | 30 | [[package]] 31 | name = "argcomplete" 32 | version = "3.5.0" 33 | requires_python = ">=3.8" 34 | summary = "Bash tab completion for argparse" 35 | groups = ["release"] 36 | files = [ 37 | {file = "argcomplete-3.5.0-py3-none-any.whl", hash = "sha256:d4bcf3ff544f51e16e54228a7ac7f486ed70ebf2ecfe49a63a91171c76bf029b"}, 38 | {file = "argcomplete-3.5.0.tar.gz", hash = "sha256:4349400469dccfb7950bb60334a680c58d88699bff6159df61251878dc6bf74b"}, 39 | ] 40 | 41 | [[package]] 42 | name = "blinker" 43 | version = "1.8.2" 44 | requires_python = ">=3.8" 45 | summary = "Fast, simple object-to-object and broadcast signaling" 46 | groups = ["default", "test"] 47 | files = [ 48 | {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, 49 | {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, 50 | ] 51 | 52 | [[package]] 53 | name = "cachetools" 54 | version = "5.5.0" 55 | requires_python = ">=3.7" 56 | summary = "Extensible memoizing collections and decorators" 57 | groups = ["tox"] 58 | files = [ 59 | {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, 60 | {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, 61 | ] 62 | 63 | [[package]] 64 | name = "certifi" 65 | version = "2024.8.30" 66 | requires_python = ">=3.6" 67 | summary = "Python package for providing Mozilla's CA Bundle." 68 | groups = ["default", "test"] 69 | files = [ 70 | {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, 71 | {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, 72 | ] 73 | 74 | [[package]] 75 | name = "chardet" 76 | version = "5.2.0" 77 | requires_python = ">=3.7" 78 | summary = "Universal encoding detector for Python 3" 79 | groups = ["tox"] 80 | files = [ 81 | {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, 82 | {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, 83 | ] 84 | 85 | [[package]] 86 | name = "charset-normalizer" 87 | version = "3.3.2" 88 | requires_python = ">=3.7.0" 89 | summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 90 | groups = ["release"] 91 | files = [ 92 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 93 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 94 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 95 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 96 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 97 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 98 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 99 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 100 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 101 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 102 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 103 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 104 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 105 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 106 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 107 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 108 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 109 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 110 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 111 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 112 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 113 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 114 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 115 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 116 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 117 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 118 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 119 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 120 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 121 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 122 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 123 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 124 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 125 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 126 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 127 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 128 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 129 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 130 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 131 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 132 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 133 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 134 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 135 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 136 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 137 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 138 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 139 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 140 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 141 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 142 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 143 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 144 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 145 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 146 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 147 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 148 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 149 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 150 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 151 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 152 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 153 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 154 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 155 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 156 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 157 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 158 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 159 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 160 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 161 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 162 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 163 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 164 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 165 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 166 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 167 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 168 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 169 | ] 170 | 171 | [[package]] 172 | name = "codespell" 173 | version = "2.3.0" 174 | requires_python = ">=3.8" 175 | summary = "Codespell" 176 | groups = ["lint"] 177 | files = [ 178 | {file = "codespell-2.3.0-py3-none-any.whl", hash = "sha256:a9c7cef2501c9cfede2110fd6d4e5e62296920efe9abfb84648df866e47f58d1"}, 179 | {file = "codespell-2.3.0.tar.gz", hash = "sha256:360c7d10f75e65f67bad720af7007e1060a5d395670ec11a7ed1fed9dd17471f"}, 180 | ] 181 | 182 | [[package]] 183 | name = "colorama" 184 | version = "0.4.6" 185 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 186 | summary = "Cross-platform colored terminal text." 187 | groups = ["release", "test", "tox"] 188 | files = [ 189 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 190 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 191 | ] 192 | 193 | [[package]] 194 | name = "commitizen" 195 | version = "3.29.0" 196 | requires_python = ">=3.8" 197 | summary = "Python commitizen client tool" 198 | groups = ["release"] 199 | dependencies = [ 200 | "argcomplete<3.6,>=1.12.1", 201 | "charset-normalizer<4,>=2.1.0", 202 | "colorama<0.5.0,>=0.4.1", 203 | "decli<0.7.0,>=0.6.0", 204 | "importlib-metadata<9,>=8.0.0; python_version < \"3.10\"", 205 | "jinja2>=2.10.3", 206 | "packaging>=19", 207 | "pyyaml>=3.08", 208 | "questionary<3.0,>=2.0", 209 | "termcolor<3,>=1.1", 210 | "tomlkit<1.0.0,>=0.5.3", 211 | "typing-extensions<5.0.0,>=4.0.1; python_version < \"3.8\"", 212 | ] 213 | files = [ 214 | {file = "commitizen-3.29.0-py3-none-any.whl", hash = "sha256:0c6c479dbee6d19292315c6fca3782cf5c1f7f1638bc4bb5ab4cfb67f4e11894"}, 215 | {file = "commitizen-3.29.0.tar.gz", hash = "sha256:586b30c1976850d244b836cd4730771097ba362c9c1684d1f8c379176c2ea532"}, 216 | ] 217 | 218 | [[package]] 219 | name = "coverage" 220 | version = "7.6.1" 221 | requires_python = ">=3.8" 222 | summary = "Code coverage measurement for Python" 223 | groups = ["test"] 224 | files = [ 225 | {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, 226 | {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, 227 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, 228 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, 229 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, 230 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, 231 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, 232 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, 233 | {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, 234 | {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, 235 | {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, 236 | {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, 237 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, 238 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, 239 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, 240 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, 241 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, 242 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, 243 | {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, 244 | {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, 245 | {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, 246 | {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, 247 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, 248 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, 249 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, 250 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, 251 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, 252 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, 253 | {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, 254 | {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, 255 | {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, 256 | {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, 257 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, 258 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, 259 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, 260 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, 261 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, 262 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, 263 | {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, 264 | {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, 265 | {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, 266 | {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, 267 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, 268 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, 269 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, 270 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, 271 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, 272 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, 273 | {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, 274 | {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, 275 | {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, 276 | {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, 277 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, 278 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, 279 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, 280 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, 281 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, 282 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, 283 | {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, 284 | {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, 285 | {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, 286 | {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, 287 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, 288 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, 289 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, 290 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, 291 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, 292 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, 293 | {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, 294 | {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, 295 | {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, 296 | {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, 297 | ] 298 | 299 | [[package]] 300 | name = "coverage" 301 | version = "7.6.1" 302 | extras = ["toml"] 303 | requires_python = ">=3.8" 304 | summary = "Code coverage measurement for Python" 305 | groups = ["test"] 306 | dependencies = [ 307 | "coverage==7.6.1", 308 | "tomli; python_full_version <= \"3.11.0a6\"", 309 | ] 310 | files = [ 311 | {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, 312 | {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, 313 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, 314 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, 315 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, 316 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, 317 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, 318 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, 319 | {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, 320 | {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, 321 | {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, 322 | {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, 323 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, 324 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, 325 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, 326 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, 327 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, 328 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, 329 | {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, 330 | {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, 331 | {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, 332 | {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, 333 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, 334 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, 335 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, 336 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, 337 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, 338 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, 339 | {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, 340 | {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, 341 | {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, 342 | {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, 343 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, 344 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, 345 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, 346 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, 347 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, 348 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, 349 | {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, 350 | {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, 351 | {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, 352 | {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, 353 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, 354 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, 355 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, 356 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, 357 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, 358 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, 359 | {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, 360 | {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, 361 | {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, 362 | {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, 363 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, 364 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, 365 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, 366 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, 367 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, 368 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, 369 | {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, 370 | {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, 371 | {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, 372 | {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, 373 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, 374 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, 375 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, 376 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, 377 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, 378 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, 379 | {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, 380 | {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, 381 | {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, 382 | {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, 383 | ] 384 | 385 | [[package]] 386 | name = "decli" 387 | version = "0.6.2" 388 | requires_python = ">=3.7" 389 | summary = "Minimal, easy-to-use, declarative cli tool" 390 | groups = ["release"] 391 | files = [ 392 | {file = "decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed"}, 393 | {file = "decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f"}, 394 | ] 395 | 396 | [[package]] 397 | name = "dep-logic" 398 | version = "0.4.9" 399 | requires_python = ">=3.8" 400 | summary = "Python dependency specifications supporting logical operations" 401 | groups = ["default", "test"] 402 | dependencies = [ 403 | "packaging>=22", 404 | ] 405 | files = [ 406 | {file = "dep_logic-0.4.9-py3-none-any.whl", hash = "sha256:06faa33814e5ff881922f644284a608d7da7946462760f710217d829ae864a0e"}, 407 | {file = "dep_logic-0.4.9.tar.gz", hash = "sha256:5d455ea2a3da4fea2be6186d886905c57eeeebe3ea7fa967f599cb8e0f01d5c9"}, 408 | ] 409 | 410 | [[package]] 411 | name = "distlib" 412 | version = "0.3.8" 413 | summary = "Distribution utilities" 414 | groups = ["default", "test", "tox"] 415 | files = [ 416 | {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, 417 | {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, 418 | ] 419 | 420 | [[package]] 421 | name = "emotional" 422 | version = "0.5.1" 423 | requires_python = ">=3.8.1" 424 | summary = "A Commitizen plugin for conventional commit with emojis and integrations" 425 | groups = ["release"] 426 | dependencies = [ 427 | "commitizen>=3.18.1", 428 | "typing-extensions>=4.4.0; python_version < \"3.10\"", 429 | ] 430 | files = [ 431 | {file = "emotional-0.5.1-py3-none-any.whl", hash = "sha256:1a7d26d268c6566d12d597a8f582b7b26c463b0041164648426a7a2cd5437493"}, 432 | {file = "emotional-0.5.1.tar.gz", hash = "sha256:a68eb30e6e8aa9e5196338bc8c85b2351386e5f066443ead67046e680b8d6566"}, 433 | ] 434 | 435 | [[package]] 436 | name = "exceptiongroup" 437 | version = "1.2.2" 438 | requires_python = ">=3.7" 439 | summary = "Backport of PEP 654 (exception groups)" 440 | groups = ["default", "test"] 441 | marker = "python_version < \"3.11\"" 442 | files = [ 443 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 444 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 445 | ] 446 | 447 | [[package]] 448 | name = "filelock" 449 | version = "3.16.1" 450 | requires_python = ">=3.8" 451 | summary = "A platform independent file lock." 452 | groups = ["default", "test", "tox"] 453 | files = [ 454 | {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, 455 | {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, 456 | ] 457 | 458 | [[package]] 459 | name = "findpython" 460 | version = "0.6.1" 461 | requires_python = ">=3.8" 462 | summary = "A utility to find python versions on your system" 463 | groups = ["default", "test"] 464 | dependencies = [ 465 | "packaging>=20", 466 | ] 467 | files = [ 468 | {file = "findpython-0.6.1-py3-none-any.whl", hash = "sha256:1fb4d709205de185b0561900267dfff64a841c910fe28d6038b2394ff925a81a"}, 469 | {file = "findpython-0.6.1.tar.gz", hash = "sha256:56e52b409a92bcbd495cf981c85acf137f3b3e51cc769b46eba219bb1ab7533c"}, 470 | ] 471 | 472 | [[package]] 473 | name = "h11" 474 | version = "0.14.0" 475 | requires_python = ">=3.7" 476 | summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 477 | groups = ["default", "test"] 478 | dependencies = [ 479 | "typing-extensions; python_version < \"3.8\"", 480 | ] 481 | files = [ 482 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 483 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 484 | ] 485 | 486 | [[package]] 487 | name = "hishel" 488 | version = "0.0.31" 489 | requires_python = ">=3.8" 490 | summary = "Persistent cache implementation for httpx and httpcore" 491 | groups = ["default", "test"] 492 | dependencies = [ 493 | "httpx>=0.22.0", 494 | "typing-extensions>=4.8.0", 495 | ] 496 | files = [ 497 | {file = "hishel-0.0.31-py3-none-any.whl", hash = "sha256:8c0b4b3e6861d3eb643dccbedbe84e559d8422bdc58ebfea8ea8a5419ca18f52"}, 498 | {file = "hishel-0.0.31.tar.gz", hash = "sha256:0577291a0159a466152096e2b8ac33c476707eb6a44e5dca6de5a855d7fbd38b"}, 499 | ] 500 | 501 | [[package]] 502 | name = "httpcore" 503 | version = "1.0.5" 504 | requires_python = ">=3.8" 505 | summary = "A minimal low-level HTTP client." 506 | groups = ["default", "test"] 507 | dependencies = [ 508 | "certifi", 509 | "h11<0.15,>=0.13", 510 | ] 511 | files = [ 512 | {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, 513 | {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, 514 | ] 515 | 516 | [[package]] 517 | name = "httpx" 518 | version = "0.27.2" 519 | requires_python = ">=3.8" 520 | summary = "The next generation HTTP client." 521 | groups = ["default", "test"] 522 | dependencies = [ 523 | "anyio", 524 | "certifi", 525 | "httpcore==1.*", 526 | "idna", 527 | "sniffio", 528 | ] 529 | files = [ 530 | {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, 531 | {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, 532 | ] 533 | 534 | [[package]] 535 | name = "httpx" 536 | version = "0.27.2" 537 | extras = ["socks"] 538 | requires_python = ">=3.8" 539 | summary = "The next generation HTTP client." 540 | groups = ["default", "test"] 541 | dependencies = [ 542 | "httpx==0.27.2", 543 | "socksio==1.*", 544 | ] 545 | files = [ 546 | {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, 547 | {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, 548 | ] 549 | 550 | [[package]] 551 | name = "idna" 552 | version = "3.10" 553 | requires_python = ">=3.6" 554 | summary = "Internationalized Domain Names in Applications (IDNA)" 555 | groups = ["default", "test"] 556 | files = [ 557 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 558 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 559 | ] 560 | 561 | [[package]] 562 | name = "importlib-metadata" 563 | version = "8.5.0" 564 | requires_python = ">=3.8" 565 | summary = "Read metadata from Python packages" 566 | groups = ["default", "release", "test"] 567 | marker = "python_version < \"3.10\"" 568 | dependencies = [ 569 | "typing-extensions>=3.6.4; python_version < \"3.8\"", 570 | "zipp>=3.20", 571 | ] 572 | files = [ 573 | {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, 574 | {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, 575 | ] 576 | 577 | [[package]] 578 | name = "importlib-resources" 579 | version = "6.4.5" 580 | requires_python = ">=3.8" 581 | summary = "Read resources from Python packages" 582 | groups = ["default", "test"] 583 | marker = "python_version < \"3.9\"" 584 | dependencies = [ 585 | "zipp>=3.1.0; python_version < \"3.10\"", 586 | ] 587 | files = [ 588 | {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, 589 | {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, 590 | ] 591 | 592 | [[package]] 593 | name = "iniconfig" 594 | version = "2.0.0" 595 | requires_python = ">=3.7" 596 | summary = "brain-dead simple config-ini parsing" 597 | groups = ["test"] 598 | files = [ 599 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 600 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 601 | ] 602 | 603 | [[package]] 604 | name = "installer" 605 | version = "0.7.0" 606 | requires_python = ">=3.7" 607 | summary = "A library for installing Python wheels." 608 | groups = ["default", "test"] 609 | files = [ 610 | {file = "installer-0.7.0-py3-none-any.whl", hash = "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53"}, 611 | {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"}, 612 | ] 613 | 614 | [[package]] 615 | name = "jinja2" 616 | version = "3.1.4" 617 | requires_python = ">=3.7" 618 | summary = "A very fast and expressive template engine." 619 | groups = ["release"] 620 | dependencies = [ 621 | "MarkupSafe>=2.0", 622 | ] 623 | files = [ 624 | {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, 625 | {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, 626 | ] 627 | 628 | [[package]] 629 | name = "markdown-it-py" 630 | version = "3.0.0" 631 | requires_python = ">=3.8" 632 | summary = "Python port of markdown-it. Markdown parsing, done right!" 633 | groups = ["default", "test"] 634 | dependencies = [ 635 | "mdurl~=0.1", 636 | ] 637 | files = [ 638 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 639 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 640 | ] 641 | 642 | [[package]] 643 | name = "markupsafe" 644 | version = "2.1.5" 645 | requires_python = ">=3.7" 646 | summary = "Safely add untrusted strings to HTML/XML markup." 647 | groups = ["release"] 648 | files = [ 649 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, 650 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, 651 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, 652 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, 653 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, 654 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, 655 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, 656 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, 657 | {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, 658 | {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, 659 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, 660 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, 661 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, 662 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, 663 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, 664 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, 665 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, 666 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, 667 | {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, 668 | {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, 669 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, 670 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, 671 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, 672 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, 673 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, 674 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, 675 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, 676 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, 677 | {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, 678 | {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, 679 | {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, 680 | {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, 681 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, 682 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, 683 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, 684 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, 685 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, 686 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, 687 | {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, 688 | {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, 689 | {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, 690 | {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, 691 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, 692 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, 693 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, 694 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, 695 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, 696 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, 697 | {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, 698 | {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, 699 | {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, 700 | ] 701 | 702 | [[package]] 703 | name = "mdurl" 704 | version = "0.1.2" 705 | requires_python = ">=3.7" 706 | summary = "Markdown URL utilities" 707 | groups = ["default", "test"] 708 | files = [ 709 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 710 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 711 | ] 712 | 713 | [[package]] 714 | name = "msgpack" 715 | version = "1.1.0" 716 | requires_python = ">=3.8" 717 | summary = "MessagePack serializer" 718 | groups = ["default", "test"] 719 | files = [ 720 | {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, 721 | {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, 722 | {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, 723 | {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, 724 | {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, 725 | {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, 726 | {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, 727 | {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, 728 | {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, 729 | {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, 730 | {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, 731 | {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, 732 | {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, 733 | {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, 734 | {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, 735 | {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, 736 | {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, 737 | {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, 738 | {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, 739 | {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, 740 | {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, 741 | {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, 742 | {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, 743 | {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, 744 | {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, 745 | {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, 746 | {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, 747 | {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, 748 | {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, 749 | {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, 750 | {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, 751 | {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, 752 | {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, 753 | {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, 754 | {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, 755 | {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, 756 | {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, 757 | {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, 758 | {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, 759 | {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, 760 | {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, 761 | {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, 762 | {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, 763 | {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, 764 | {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, 765 | {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, 766 | {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, 767 | {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, 768 | {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, 769 | {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, 770 | {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, 771 | {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, 772 | {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, 773 | {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, 774 | {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, 775 | {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, 776 | {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, 777 | {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, 778 | {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, 779 | {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, 780 | {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, 781 | {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, 782 | {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, 783 | {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, 784 | ] 785 | 786 | [[package]] 787 | name = "mypy" 788 | version = "1.11.2" 789 | requires_python = ">=3.8" 790 | summary = "Optional static typing for Python" 791 | groups = ["lint"] 792 | dependencies = [ 793 | "mypy-extensions>=1.0.0", 794 | "tomli>=1.1.0; python_version < \"3.11\"", 795 | "typing-extensions>=4.6.0", 796 | ] 797 | files = [ 798 | {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, 799 | {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, 800 | {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, 801 | {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, 802 | {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, 803 | {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, 804 | {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, 805 | {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, 806 | {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, 807 | {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, 808 | {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, 809 | {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, 810 | {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, 811 | {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, 812 | {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, 813 | {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, 814 | {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, 815 | {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, 816 | {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, 817 | {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, 818 | {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, 819 | {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, 820 | {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, 821 | {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, 822 | {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, 823 | {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, 824 | {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, 825 | ] 826 | 827 | [[package]] 828 | name = "mypy-extensions" 829 | version = "1.0.0" 830 | requires_python = ">=3.5" 831 | summary = "Type system extensions for programs checked with the mypy type checker." 832 | groups = ["lint"] 833 | files = [ 834 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 835 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 836 | ] 837 | 838 | [[package]] 839 | name = "packaging" 840 | version = "24.1" 841 | requires_python = ">=3.8" 842 | summary = "Core utilities for Python packages" 843 | groups = ["default", "release", "test", "tox"] 844 | files = [ 845 | {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, 846 | {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, 847 | ] 848 | 849 | [[package]] 850 | name = "pbs-installer" 851 | version = "2024.9.9" 852 | requires_python = ">=3.8" 853 | summary = "Installer for Python Build Standalone" 854 | groups = ["default", "test"] 855 | files = [ 856 | {file = "pbs_installer-2024.9.9-py3-none-any.whl", hash = "sha256:0c5846d3327cb09ea2bcbbc5521f5f45fd4f0021e78e57ca670e9d251085830b"}, 857 | {file = "pbs_installer-2024.9.9.tar.gz", hash = "sha256:bed162d05ef71c53a0e5e5c6349bab1b07a3d0e5af1800d619a4414a1fda309a"}, 858 | ] 859 | 860 | [[package]] 861 | name = "pdm" 862 | version = "2.19.0" 863 | requires_python = ">=3.8" 864 | summary = "A modern Python package and dependency manager supporting the latest PEP standards" 865 | groups = ["default", "test"] 866 | dependencies = [ 867 | "blinker", 868 | "dep-logic>=0.4.4", 869 | "filelock>=3.13", 870 | "findpython<1.0.0a0,>=0.6.0", 871 | "hishel<0.1.0,>=0.0.24", 872 | "httpx[socks]<1,>0.20", 873 | "importlib-metadata>=3.6; python_version < \"3.10\"", 874 | "importlib-resources>=5; python_version < \"3.9\"", 875 | "installer<0.8,>=0.7", 876 | "msgpack>=1.0", 877 | "packaging!=22.0,>=20.9", 878 | "pbs-installer>=2024.4.18", 879 | "platformdirs", 880 | "pyproject-hooks", 881 | "python-dotenv>=0.15", 882 | "resolvelib>=1.0.1", 883 | "rich>=12.3.0", 884 | "shellingham>=1.3.2", 885 | "tomli>=1.1.0; python_version < \"3.11\"", 886 | "tomlkit<1,>=0.11.1", 887 | "truststore>=0.9; python_version >= \"3.10\"", 888 | "unearth>=0.17.0", 889 | "virtualenv>=20", 890 | ] 891 | files = [ 892 | {file = "pdm-2.19.0-py3-none-any.whl", hash = "sha256:d8d730be579025d7d978bf5c3149f278c0aece8e1e2794a6fd35c790ea7d2d7a"}, 893 | {file = "pdm-2.19.0.tar.gz", hash = "sha256:4dda3caec3a5e4d9942c9bc6f3bdcf34c60c21e76050ef35f6c27786f4b1d470"}, 894 | ] 895 | 896 | [[package]] 897 | name = "pdm" 898 | version = "2.19.0" 899 | extras = ["pytest"] 900 | requires_python = ">=3.8" 901 | summary = "A modern Python package and dependency manager supporting the latest PEP standards" 902 | groups = ["test"] 903 | dependencies = [ 904 | "pdm==2.19.0", 905 | "pytest", 906 | "pytest-mock", 907 | ] 908 | files = [ 909 | {file = "pdm-2.19.0-py3-none-any.whl", hash = "sha256:d8d730be579025d7d978bf5c3149f278c0aece8e1e2794a6fd35c790ea7d2d7a"}, 910 | {file = "pdm-2.19.0.tar.gz", hash = "sha256:4dda3caec3a5e4d9942c9bc6f3bdcf34c60c21e76050ef35f6c27786f4b1d470"}, 911 | ] 912 | 913 | [[package]] 914 | name = "platformdirs" 915 | version = "4.3.6" 916 | requires_python = ">=3.8" 917 | summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 918 | groups = ["default", "test", "tox"] 919 | files = [ 920 | {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, 921 | {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, 922 | ] 923 | 924 | [[package]] 925 | name = "pluggy" 926 | version = "1.5.0" 927 | requires_python = ">=3.8" 928 | summary = "plugin and hook calling mechanisms for python" 929 | groups = ["test", "tox"] 930 | files = [ 931 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 932 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 933 | ] 934 | 935 | [[package]] 936 | name = "prompt-toolkit" 937 | version = "3.0.36" 938 | requires_python = ">=3.6.2" 939 | summary = "Library for building powerful interactive command lines in Python" 940 | groups = ["release"] 941 | dependencies = [ 942 | "wcwidth", 943 | ] 944 | files = [ 945 | {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, 946 | {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, 947 | ] 948 | 949 | [[package]] 950 | name = "pygments" 951 | version = "2.18.0" 952 | requires_python = ">=3.8" 953 | summary = "Pygments is a syntax highlighting package written in Python." 954 | groups = ["default", "test"] 955 | files = [ 956 | {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, 957 | {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, 958 | ] 959 | 960 | [[package]] 961 | name = "pyproject-api" 962 | version = "1.8.0" 963 | requires_python = ">=3.8" 964 | summary = "API to interact with the python pyproject.toml based projects" 965 | groups = ["tox"] 966 | dependencies = [ 967 | "packaging>=24.1", 968 | "tomli>=2.0.1; python_version < \"3.11\"", 969 | ] 970 | files = [ 971 | {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, 972 | {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, 973 | ] 974 | 975 | [[package]] 976 | name = "pyproject-hooks" 977 | version = "1.1.0" 978 | requires_python = ">=3.7" 979 | summary = "Wrappers to call pyproject.toml-based build backend hooks." 980 | groups = ["default", "test"] 981 | files = [ 982 | {file = "pyproject_hooks-1.1.0-py3-none-any.whl", hash = "sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2"}, 983 | {file = "pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965"}, 984 | ] 985 | 986 | [[package]] 987 | name = "pytest" 988 | version = "8.3.3" 989 | requires_python = ">=3.8" 990 | summary = "pytest: simple powerful testing with Python" 991 | groups = ["test"] 992 | dependencies = [ 993 | "colorama; sys_platform == \"win32\"", 994 | "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", 995 | "iniconfig", 996 | "packaging", 997 | "pluggy<2,>=1.5", 998 | "tomli>=1; python_version < \"3.11\"", 999 | ] 1000 | files = [ 1001 | {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, 1002 | {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "pytest-cov" 1007 | version = "5.0.0" 1008 | requires_python = ">=3.8" 1009 | summary = "Pytest plugin for measuring coverage." 1010 | groups = ["test"] 1011 | dependencies = [ 1012 | "coverage[toml]>=5.2.1", 1013 | "pytest>=4.6", 1014 | ] 1015 | files = [ 1016 | {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, 1017 | {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "pytest-mock" 1022 | version = "3.14.0" 1023 | requires_python = ">=3.8" 1024 | summary = "Thin-wrapper around the mock package for easier use with pytest" 1025 | groups = ["test"] 1026 | dependencies = [ 1027 | "pytest>=6.2.5", 1028 | ] 1029 | files = [ 1030 | {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, 1031 | {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "pytest-sugar" 1036 | version = "1.0.0" 1037 | summary = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." 1038 | groups = ["test"] 1039 | dependencies = [ 1040 | "packaging>=21.3", 1041 | "pytest>=6.2.0", 1042 | "termcolor>=2.1.0", 1043 | ] 1044 | files = [ 1045 | {file = "pytest-sugar-1.0.0.tar.gz", hash = "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a"}, 1046 | {file = "pytest_sugar-1.0.0-py3-none-any.whl", hash = "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd"}, 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "python-dotenv" 1051 | version = "1.0.1" 1052 | requires_python = ">=3.8" 1053 | summary = "Read key-value pairs from a .env file and set them as environment variables" 1054 | groups = ["default", "test"] 1055 | files = [ 1056 | {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, 1057 | {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, 1058 | ] 1059 | 1060 | [[package]] 1061 | name = "pyyaml" 1062 | version = "6.0.2" 1063 | requires_python = ">=3.8" 1064 | summary = "YAML parser and emitter for Python" 1065 | groups = ["release"] 1066 | files = [ 1067 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 1068 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 1069 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 1070 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 1071 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 1072 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 1073 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 1074 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 1075 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 1076 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 1077 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 1078 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 1079 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 1080 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 1081 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 1082 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 1083 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 1084 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 1085 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 1086 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 1087 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 1088 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 1089 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 1090 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 1091 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 1092 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 1093 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 1094 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 1095 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 1096 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 1097 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 1098 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 1099 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 1100 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 1101 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 1102 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 1103 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 1104 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 1105 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 1106 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 1107 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 1108 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 1109 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 1110 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 1111 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 1112 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 1113 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 1114 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 1115 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 1116 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 1117 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 1118 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 1119 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 1120 | ] 1121 | 1122 | [[package]] 1123 | name = "questionary" 1124 | version = "2.0.1" 1125 | requires_python = ">=3.8" 1126 | summary = "Python library to build pretty command line user prompts ⭐️" 1127 | groups = ["release"] 1128 | dependencies = [ 1129 | "prompt-toolkit<=3.0.36,>=2.0", 1130 | ] 1131 | files = [ 1132 | {file = "questionary-2.0.1-py3-none-any.whl", hash = "sha256:8ab9a01d0b91b68444dff7f6652c1e754105533f083cbe27597c8110ecc230a2"}, 1133 | {file = "questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b"}, 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "resolvelib" 1138 | version = "1.0.1" 1139 | summary = "Resolve abstract dependencies into concrete ones" 1140 | groups = ["default", "test"] 1141 | files = [ 1142 | {file = "resolvelib-1.0.1-py2.py3-none-any.whl", hash = "sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf"}, 1143 | {file = "resolvelib-1.0.1.tar.gz", hash = "sha256:04ce76cbd63fded2078ce224785da6ecd42b9564b1390793f64ddecbe997b309"}, 1144 | ] 1145 | 1146 | [[package]] 1147 | name = "rich" 1148 | version = "13.8.1" 1149 | requires_python = ">=3.7.0" 1150 | summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 1151 | groups = ["default", "test"] 1152 | dependencies = [ 1153 | "markdown-it-py>=2.2.0", 1154 | "pygments<3.0.0,>=2.13.0", 1155 | "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"", 1156 | ] 1157 | files = [ 1158 | {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, 1159 | {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, 1160 | ] 1161 | 1162 | [[package]] 1163 | name = "ruff" 1164 | version = "0.6.7" 1165 | requires_python = ">=3.7" 1166 | summary = "An extremely fast Python linter and code formatter, written in Rust." 1167 | groups = ["lint"] 1168 | files = [ 1169 | {file = "ruff-0.6.7-py3-none-linux_armv6l.whl", hash = "sha256:08277b217534bfdcc2e1377f7f933e1c7957453e8a79764d004e44c40db923f2"}, 1170 | {file = "ruff-0.6.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c6707a32e03b791f4448dc0dce24b636cbcdee4dd5607adc24e5ee73fd86c00a"}, 1171 | {file = "ruff-0.6.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:533d66b7774ef224e7cf91506a7dafcc9e8ec7c059263ec46629e54e7b1f90ab"}, 1172 | {file = "ruff-0.6.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17a86aac6f915932d259f7bec79173e356165518859f94649d8c50b81ff087e9"}, 1173 | {file = "ruff-0.6.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3f8822defd260ae2460ea3832b24d37d203c3577f48b055590a426a722d50ef"}, 1174 | {file = "ruff-0.6.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ba4efe5c6dbbb58be58dd83feedb83b5e95c00091bf09987b4baf510fee5c99"}, 1175 | {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:525201b77f94d2b54868f0cbe5edc018e64c22563da6c5c2e5c107a4e85c1c0d"}, 1176 | {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8854450839f339e1049fdbe15d875384242b8e85d5c6947bb2faad33c651020b"}, 1177 | {file = "ruff-0.6.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f0b62056246234d59cbf2ea66e84812dc9ec4540518e37553513392c171cb18"}, 1178 | {file = "ruff-0.6.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b1462fa56c832dc0cea5b4041cfc9c97813505d11cce74ebc6d1aae068de36b"}, 1179 | {file = "ruff-0.6.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:02b083770e4cdb1495ed313f5694c62808e71764ec6ee5db84eedd82fd32d8f5"}, 1180 | {file = "ruff-0.6.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c05fd37013de36dfa883a3854fae57b3113aaa8abf5dea79202675991d48624"}, 1181 | {file = "ruff-0.6.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f49c9caa28d9bbfac4a637ae10327b3db00f47d038f3fbb2195c4d682e925b14"}, 1182 | {file = "ruff-0.6.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a0e1655868164e114ba43a908fd2d64a271a23660195017c17691fb6355d59bb"}, 1183 | {file = "ruff-0.6.7-py3-none-win32.whl", hash = "sha256:a939ca435b49f6966a7dd64b765c9df16f1faed0ca3b6f16acdf7731969deb35"}, 1184 | {file = "ruff-0.6.7-py3-none-win_amd64.whl", hash = "sha256:590445eec5653f36248584579c06252ad2e110a5d1f32db5420de35fb0e1c977"}, 1185 | {file = "ruff-0.6.7-py3-none-win_arm64.whl", hash = "sha256:b28f0d5e2f771c1fe3c7a45d3f53916fc74a480698c4b5731f0bea61e52137c8"}, 1186 | {file = "ruff-0.6.7.tar.gz", hash = "sha256:44e52129d82266fa59b587e2cd74def5637b730a69c4542525dfdecfaae38bd5"}, 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "shellcheck-py" 1191 | version = "0.10.0.1" 1192 | requires_python = ">=3.8" 1193 | summary = "Python wrapper around invoking shellcheck (https://www.shellcheck.net/)" 1194 | groups = ["test"] 1195 | files = [ 1196 | {file = "shellcheck_py-0.10.0.1-py2.py3-none-macosx_11_0_x86_64.whl", hash = "sha256:48f08965cafbb3363b265c4ef40628ffced19cb6fc7c4bb5ce72d32cbcfb4bb9"}, 1197 | {file = "shellcheck_py-0.10.0.1-py2.py3-none-macosx_14_0_arm64.whl", hash = "sha256:8f3bf12ee6d0845dd5ac1a7bac8c4b1fec0379e115950986883c9488af40ada7"}, 1198 | {file = "shellcheck_py-0.10.0.1-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1c266f7f54cd286057c592ead3095f93d123acdcabf048879a7d8900c3aac7b"}, 1199 | {file = "shellcheck_py-0.10.0.1-py2.py3-none-win_amd64.whl", hash = "sha256:be73a16931c05f79643ff74b6519d1e1203b394583ab8c68a48a8e7f257d1090"}, 1200 | {file = "shellcheck_py-0.10.0.1.tar.gz", hash = "sha256:390826b340b8c19173922b0da5ef7b66ef34d4d087dc48aad3e01f7e77e164d9"}, 1201 | ] 1202 | 1203 | [[package]] 1204 | name = "shellingham" 1205 | version = "1.5.4" 1206 | requires_python = ">=3.7" 1207 | summary = "Tool to Detect Surrounding Shell" 1208 | groups = ["default", "test"] 1209 | files = [ 1210 | {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, 1211 | {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, 1212 | ] 1213 | 1214 | [[package]] 1215 | name = "sniffio" 1216 | version = "1.3.1" 1217 | requires_python = ">=3.7" 1218 | summary = "Sniff out which async library your code is running under" 1219 | groups = ["default", "test"] 1220 | files = [ 1221 | {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, 1222 | {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, 1223 | ] 1224 | 1225 | [[package]] 1226 | name = "socksio" 1227 | version = "1.0.0" 1228 | requires_python = ">=3.6" 1229 | summary = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5." 1230 | groups = ["default", "test"] 1231 | files = [ 1232 | {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"}, 1233 | {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"}, 1234 | ] 1235 | 1236 | [[package]] 1237 | name = "syrupy" 1238 | version = "4.7.1" 1239 | requires_python = ">=3.8.1" 1240 | summary = "Pytest Snapshot Test Utility" 1241 | groups = ["test"] 1242 | dependencies = [ 1243 | "pytest<9.0.0,>=7.0.0", 1244 | ] 1245 | files = [ 1246 | {file = "syrupy-4.7.1-py3-none-any.whl", hash = "sha256:be002267a512a4bedddfae2e026c93df1ea928ae10baadc09640516923376d41"}, 1247 | {file = "syrupy-4.7.1.tar.gz", hash = "sha256:f9d4485f3f27d0e5df6ed299cac6fa32eb40a441915d988e82be5a4bdda335c8"}, 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "termcolor" 1252 | version = "2.4.0" 1253 | requires_python = ">=3.8" 1254 | summary = "ANSI color formatting for output in terminal" 1255 | groups = ["release", "test"] 1256 | files = [ 1257 | {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, 1258 | {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "tomli" 1263 | version = "2.0.1" 1264 | requires_python = ">=3.7" 1265 | summary = "A lil' TOML parser" 1266 | groups = ["default", "lint", "test", "tox"] 1267 | marker = "python_version < \"3.11\"" 1268 | files = [ 1269 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 1270 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 1271 | ] 1272 | 1273 | [[package]] 1274 | name = "tomlkit" 1275 | version = "0.13.2" 1276 | requires_python = ">=3.8" 1277 | summary = "Style preserving TOML library" 1278 | groups = ["default", "release", "test"] 1279 | files = [ 1280 | {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, 1281 | {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "tox" 1286 | version = "4.20.0" 1287 | requires_python = ">=3.8" 1288 | summary = "tox is a generic virtualenv management and test command line tool" 1289 | groups = ["tox"] 1290 | dependencies = [ 1291 | "cachetools>=5.5", 1292 | "chardet>=5.2", 1293 | "colorama>=0.4.6", 1294 | "filelock>=3.15.4", 1295 | "packaging>=24.1", 1296 | "platformdirs>=4.2.2", 1297 | "pluggy>=1.5", 1298 | "pyproject-api>=1.7.1", 1299 | "tomli>=2.0.1; python_version < \"3.11\"", 1300 | "virtualenv>=20.26.3", 1301 | ] 1302 | files = [ 1303 | {file = "tox-4.20.0-py3-none-any.whl", hash = "sha256:21a8005e3d3fe5658a8e36b8ca3ed13a4230429063c5cc2a2fdac6ee5aa0de34"}, 1304 | {file = "tox-4.20.0.tar.gz", hash = "sha256:5b78a49b6eaaeab3ae4186415e7c97d524f762ae967c63562687c3e5f0ec23d5"}, 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "tox-pdm" 1309 | version = "0.7.2" 1310 | requires_python = ">=3.7" 1311 | summary = "A plugin for tox that utilizes PDM as the package manager and installer" 1312 | groups = ["tox"] 1313 | dependencies = [ 1314 | "tomli; python_version < \"3.11\"", 1315 | "tox>=4.0", 1316 | ] 1317 | files = [ 1318 | {file = "tox_pdm-0.7.2-py3-none-any.whl", hash = "sha256:12f6215416b7acd00a80a9e7128f3dc3e3c89308d60707f5d0a24abdf83ac104"}, 1319 | {file = "tox_pdm-0.7.2.tar.gz", hash = "sha256:a841a7e1e942a71805624703b9a6d286663bd6af79bba6130ba756975c315308"}, 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "truststore" 1324 | version = "0.9.2" 1325 | requires_python = ">=3.10" 1326 | summary = "Verify certificates using native system trust stores" 1327 | groups = ["default", "test"] 1328 | marker = "python_version >= \"3.10\"" 1329 | files = [ 1330 | {file = "truststore-0.9.2-py3-none-any.whl", hash = "sha256:04559916f8810cc1a5ecc41f215eddc988746067b754fc0995da7a2ceaf54735"}, 1331 | {file = "truststore-0.9.2.tar.gz", hash = "sha256:a1dee0d0575ff22d2875476343783a5d64575419974e228f3248772613c3d993"}, 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "typing-extensions" 1336 | version = "4.12.2" 1337 | requires_python = ">=3.8" 1338 | summary = "Backported and Experimental Type Hints for Python 3.8+" 1339 | groups = ["default", "lint", "release", "test"] 1340 | files = [ 1341 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, 1342 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "unearth" 1347 | version = "0.17.2" 1348 | requires_python = ">=3.8" 1349 | summary = "A utility to fetch and download python packages" 1350 | groups = ["default", "test"] 1351 | dependencies = [ 1352 | "httpx<1,>=0.27.0", 1353 | "packaging>=20", 1354 | ] 1355 | files = [ 1356 | {file = "unearth-0.17.2-py3-none-any.whl", hash = "sha256:4d21af1238a583835fca156322f7225382e718cdcc42d6278050a88e605c4ad5"}, 1357 | {file = "unearth-0.17.2.tar.gz", hash = "sha256:0b8a2afd3476f1ab6155fc579501ac47fffe43547d88a70e5a5b76a7fe6caa2c"}, 1358 | ] 1359 | 1360 | [[package]] 1361 | name = "virtualenv" 1362 | version = "20.26.5" 1363 | requires_python = ">=3.7" 1364 | summary = "Virtual Python Environment builder" 1365 | groups = ["default", "test", "tox"] 1366 | dependencies = [ 1367 | "distlib<1,>=0.3.7", 1368 | "filelock<4,>=3.12.2", 1369 | "importlib-metadata>=6.6; python_version < \"3.8\"", 1370 | "platformdirs<5,>=3.9.1", 1371 | ] 1372 | files = [ 1373 | {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, 1374 | {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, 1375 | ] 1376 | 1377 | [[package]] 1378 | name = "wcwidth" 1379 | version = "0.2.13" 1380 | summary = "Measures the displayed width of unicode strings in a terminal" 1381 | groups = ["release"] 1382 | dependencies = [ 1383 | "backports-functools-lru-cache>=1.2.1; python_version < \"3.2\"", 1384 | ] 1385 | files = [ 1386 | {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, 1387 | {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, 1388 | ] 1389 | 1390 | [[package]] 1391 | name = "zipp" 1392 | version = "3.20.2" 1393 | requires_python = ">=3.8" 1394 | summary = "Backport of pathlib-compatible object wrapper for zip files" 1395 | groups = ["default", "release", "test"] 1396 | marker = "python_version < \"3.10\"" 1397 | files = [ 1398 | {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, 1399 | {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, 1400 | ] 1401 | --------------------------------------------------------------------------------