├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── disl.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── action.yml ├── docker_image_size_limit ├── __init__.py ├── __main__.py ├── py.typed └── version.py ├── poetry.lock ├── pyproject.toml ├── scripts └── entrypoint.sh ├── setup.cfg └── tests ├── conftest.py ├── test_check_max_layers.py ├── test_check_size.py ├── test_cli.py ├── test_main.py └── test_version.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # Check http://editorconfig.org for more information 2 | # This is the main config file for this project: 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | indent_style = space 10 | insert_final_newline = true 11 | indent_size = 2 12 | 13 | [*.py] 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "02:00" 8 | open-pull-requests-limit: 10 9 | - package-ecosystem: github-actions 10 | directory: "/" 11 | schedule: 12 | interval: daily 13 | time: "02:00" 14 | open-pull-requests-limit: 10 15 | -------------------------------------------------------------------------------- /.github/workflows/disl.yml: -------------------------------------------------------------------------------- 1 | name: disl 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: 'Build named dockerfile' 17 | run: | 18 | docker build . --tag wemake-services/docker-image-size-limit:latest 19 | - uses: wemake-services/docker-image-size-limit@master 20 | with: # we lint this image by itself: 21 | image: 'wemake-services/docker-image-size-limit:latest' 22 | size: 405MB 23 | max_layers: 10 24 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.head_ref || github.run_id }} 12 | cancel-in-progress: true 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | build: 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Set up Python ${{ matrix.python-version }} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | - name: Install poetry 33 | run: | 34 | curl -sSL "https://install.python-poetry.org" | python - 35 | 36 | # Adding `poetry` to `$PATH`: 37 | echo "$HOME/.poetry/bin" >> $GITHUB_PATH 38 | 39 | - name: Install dependencies 40 | run: | 41 | poetry config virtualenvs.in-project true 42 | poetry run pip install -U pip 43 | poetry install 44 | 45 | - name: Pull test deps 46 | run: | 47 | # We need this specific image for tests: 48 | poetry run docker pull python:3.6.6-alpine 49 | 50 | - name: "Run checks for python ${{ matrix.python-version }}" 51 | run: | 52 | poetry run flake8 . 53 | poetry run mypy . 54 | poetry run pytest 55 | poetry run pip check 56 | 57 | - name: Upload coverage to Codecov 58 | uses: codecov/codecov-action@v5 59 | with: 60 | files: ./coverage.xml 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #### joe made this: http://goel.io/joe 2 | #### python #### 3 | # Byte-compiled / optimized / DLL files 4 | .pytest_cache 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | pip-wheel-metadata/ 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 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # mkdocs documentation 100 | /site 101 | 102 | # mypy 103 | .mypy_cache/ 104 | #### macos #### 105 | # General 106 | *.DS_Store 107 | .AppleDouble 108 | .LSOverride 109 | 110 | # Icon must end with two \r 111 | Icon 112 | 113 | 114 | # Thumbnails 115 | ._* 116 | 117 | # Files that might appear in the root of a volume 118 | .DocumentRevisions-V100 119 | .fseventsd 120 | .Spotlight-V100 121 | .TemporaryItems 122 | .Trashes 123 | .VolumeIcon.icns 124 | .com.apple.timemachine.donotpresent 125 | 126 | # Directories potentially created on remote AFP share 127 | .AppleDB 128 | .AppleDesktop 129 | Network Trash Folder 130 | Temporary Items 131 | .apdisk 132 | #### windows #### 133 | # Windows thumbnail cache files 134 | Thumbs.db 135 | ehthumbs.db 136 | ehthumbs_vista.db 137 | 138 | # Dump file 139 | *.stackdump 140 | 141 | # Folder config file 142 | Desktop.ini 143 | 144 | # Recycle Bin used on file shares 145 | $RECYCLE.BIN/ 146 | 147 | # Windows Installer files 148 | *.cab 149 | *.msi 150 | *.msm 151 | *.msp 152 | 153 | # Windows shortcuts 154 | *.lnk 155 | #### linux #### 156 | *~ 157 | 158 | # temporary files which can be created if a process still has a handle open of a deleted file 159 | .fuse_hidden* 160 | 161 | # KDE directory preferences 162 | .directory 163 | 164 | # Linux trash folder which might appear on any partition or disk 165 | .Trash-* 166 | 167 | # .nfs files are created when an open file is removed but is still being accessed 168 | .nfs* 169 | #### jetbrains #### 170 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 171 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 172 | 173 | # User-specific stuff: 174 | .idea/ 175 | 176 | ## File-based project format: 177 | *.iws 178 | 179 | ## Plugin-specific files: 180 | 181 | # IntelliJ 182 | /out/ 183 | 184 | # mpeltonen/sbt-idea plugin 185 | .idea_modules/ 186 | 187 | # JIRA plugin 188 | atlassian-ide-plugin.xml 189 | 190 | # Cursive Clojure plugin 191 | .idea/replstate.xml 192 | 193 | # Crashlytics plugin (for Android Studio and IntelliJ) 194 | com_crashlytics_export_strings.xml 195 | crashlytics.properties 196 | crashlytics-build.properties 197 | fabric.properties 198 | 199 | #### custom #### 200 | 201 | .mutmut-cache 202 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version history 2 | 3 | We follow Semantic Version. 4 | 5 | 6 | ## Version 2.1.0 7 | 8 | ### Features 9 | 10 | - Adds `--current-size` flag to show the current size of the docker image 11 | - Adds `--exit-zero` flag to force the exit code 12 | to be 0 even if there are errors 13 | - Adds `__main__.py` entrypoint to be able to run 14 | it via `python -m docker_image_size_limit` 15 | 16 | 17 | ## Version 2.0.0 18 | 19 | ### Features 20 | 21 | - Adds `--max-layers` flag to lint the maximum number of layers 22 | 23 | 24 | ## Version 1.1.0 25 | 26 | ### Features 27 | 28 | - Drops `python3.8` support 29 | 30 | ### Misc 31 | 32 | - Unlocked `urllib3`, `requests`, and `docker-py` 33 | 34 | 35 | ## Version 1.0.1 36 | 37 | ### Misc 38 | 39 | - Locked version of `urllib3` to `<2` as workaround for https://github.com/docker/docker-py/issues/3113 40 | - Locked version of `requests` to `<2.29` as workaround for https://github.com/docker/docker-py/issues/3113 41 | 42 | 43 | ## Version 1.0.0 44 | 45 | ### Features 46 | 47 | - Drops `python3.7` support 48 | - Adds `python3.11` support 49 | 50 | 51 | ## Version 0.5.0 52 | 53 | ### Features 54 | 55 | - Drops `python3.6` support 56 | - Adds `python3.10` support 57 | 58 | 59 | ## Version 0.4.1 60 | 61 | ## Misc 62 | 63 | - Updates a lot of dependencies 64 | 65 | 66 | ## Version 0.4.0 67 | 68 | ### Features 69 | 70 | - Adds `python3.9` support 71 | 72 | ## Misc 73 | 74 | - Updates a lot of dev dependencies 75 | 76 | 77 | ## Version 0.3.0 78 | 79 | ### Features 80 | 81 | - Adds Github Action support 82 | - Adds `python3.8` support 83 | - Adds `Dockerfile` and dockerhub integration 84 | 85 | ### Misc 86 | 87 | - Updates `poetry` version 88 | - Updates lots of dependencies 89 | 90 | 91 | ## Version 0.2.0 92 | 93 | ### Features 94 | 95 | - Now real negative size is returned when limit is bigger than image 96 | - Adds `py.typed` file to package the type annotation with the code 97 | 98 | ### Misc 99 | 100 | - Updates `wemake-python-styleguide` 101 | - Uses `nitpick` 102 | - Adds more docs 103 | 104 | 105 | ## Version 0.1.0 106 | 107 | - Initial release 108 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | 4 | ## Dependencies 5 | 6 | We use [poetry](https://github.com/sdispater/poetry) to manage the dependencies. 7 | 8 | To install them you would need to run `install` command: 9 | 10 | ```bash 11 | poetry install 12 | ``` 13 | 14 | To activate your `virtualenv` run `poetry shell`. 15 | 16 | 17 | ## Tests 18 | 19 | We use `pytest` and `flake8` for quality control. 20 | We also use `wemake_python_styleguide` itself 21 | to develop `wemake_python_styleguide`. 22 | 23 | To run all tests: 24 | 25 | ```bash 26 | pytest 27 | ``` 28 | 29 | To run linting: 30 | 31 | ```bash 32 | flake8 . 33 | ``` 34 | 35 | These steps are mandatory during the CI. 36 | 37 | 38 | ## Type checks 39 | 40 | We use `mypy` to run type checks on our code. 41 | To use it: 42 | 43 | ```bash 44 | mypy docker_image_size_limit 45 | ``` 46 | 47 | This step is mandatory during the CI. 48 | 49 | 50 | ## Submitting your code 51 | 52 | We use [trunk based](https://trunkbaseddevelopment.com/) 53 | development (we also sometimes call it `wemake-git-flow`). 54 | 55 | What the point of this method? 56 | 57 | 1. We use protected `master` branch, 58 | so the only way to push your code is via pull request 59 | 2. We use issue branches: to implement a new feature or to fix a bug 60 | create a new branch named `issue-$TASKNUMBER` 61 | 3. Then create a pull request to `master` branch 62 | 4. We use `git tag`s to make releases, so we can track what has changed 63 | since the latest release 64 | 65 | So, this way we achieve an easy and scalable development process 66 | which frees us from merging hell and long-living branches. 67 | 68 | In this method, the latest version of the app is always in the `master` branch. 69 | 70 | ### Before submitting 71 | 72 | Before submitting your code please do the following steps: 73 | 74 | 1. Run `pytest` to make sure everything was working before 75 | 2. Add any changes you want 76 | 3. Add tests for the new changes 77 | 4. Edit documentation if you have changed something significant 78 | 5. Update `CHANGELOG.md` with a quick summary of your changes 79 | 6. Run `pytest` again to make sure it is still working 80 | 7. Run `mypy` to ensure that types are correct 81 | 8. Run `flake8` to ensure that style is correct 82 | 83 | 84 | ## Other help 85 | 86 | You can contribute by spreading a word about this library. 87 | It would also be a huge contribution to write 88 | a short article on how you are using this project. 89 | You can also share your best practices with us. 90 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # ======================================== 2 | # = Warning! = 3 | # ======================================== 4 | # This is Github Action docker-based image. 5 | # It is not intended for local development! 6 | # 7 | # It can still be used as a raw image for your own containers. 8 | # See `action.yml` in case you want to learn more about Github Actions. 9 | # See it live: 10 | # https://github.com/wemake-services/docker-image-size-limit/actions 11 | # 12 | # This image is also available on Dockerhub: 13 | # https://hub.docker.com/r/wemakeservices/docker-image-size-limit 14 | 15 | FROM python:3.12.6-alpine 16 | 17 | LABEL maintainer="mail@sobolevn.me" 18 | LABEL vendor="wemake.services" 19 | 20 | # Our own tool: 21 | ENV DISL_VERSION='2.1.0' 22 | 23 | RUN apk add --no-cache bash docker 24 | RUN pip3 install "docker-image-size-limit==$DISL_VERSION" 25 | 26 | COPY ./scripts/entrypoint.sh / 27 | RUN chmod +x /entrypoint.sh 28 | ENTRYPOINT ["/entrypoint.sh"] 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 wemake.services 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-image-size-limit 2 | 3 | [![wemake.services](https://img.shields.io/badge/%20-wemake.services-green.svg?label=%20&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC%2FxhBQAAAAFzUkdCAK7OHOkAAAAbUExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP%2F%2F%2F5TvxDIAAAAIdFJOUwAjRA8xXANAL%2Bv0SAAAADNJREFUGNNjYCAIOJjRBdBFWMkVQeGzcHAwksJnAPPZGOGAASzPzAEHEGVsLExQwE7YswCb7AFZSF3bbAAAAABJRU5ErkJggg%3D%3D)](https://wemake.services) 4 | [![Build status](https://github.com/wemake-services/docker-image-size-limit/workflows/test/badge.svg?branch=master&event=push)](https://github.com/wemake-services/docker-image-size-limit/actions?query=workflow%3Atest) 5 | [![codecov](https://codecov.io/gh/wemake-services/docker-image-size-limit/branch/master/graph/badge.svg)](https://codecov.io/gh/wemake-services/docker-image-size-limit) 6 | [![Python Version](https://img.shields.io/pypi/pyversions/docker-image-size-limit.svg)](https://pypi.org/project/docker-image-size-limit/) 7 | [![wemake-python-styleguide](https://img.shields.io/badge/style-wemake-000000.svg)](https://github.com/wemake-services/wemake-python-styleguide) 8 | 9 | Limit your `docker` image size with a simple CLI command. 10 | Perfect to be used inside your CI process. 11 | 12 | Read the [announcing post](https://sobolevn.me/2019/03/announcing-docker-image-size-limit). 13 | 14 | 15 | ## Installation 16 | 17 | ```bash 18 | pip install docker-image-size-limit 19 | ``` 20 | 21 | Or use our [Github Action](https://github.com/wemake-services/docker-image-size-limit#github-action) or [pre-built docker image](https://github.com/wemake-services/docker-image-size-limit#docker-image). 22 | 23 | 24 | ## Usage 25 | 26 | We support just a single command: 27 | 28 | ```bash 29 | $ disl your-image-name:label 300MiB 30 | your-image-name:label exceeds 300MiB limit by 114.4 MiB 31 | ``` 32 | 33 | Add `--max-layers` flag to also lint the maximum amount of layers possible 34 | in your image: 35 | 36 | ```bash 37 | # If your image has 7 layers: 38 | $ disl your-image-name:label 300MiB --max-layers=5 39 | your-image-name:label exceeds 5 maximum layers by 2 40 | 41 | # If your image has 5 layers: 42 | $ disl your-image-name:label 300MiB --max-layers=5 43 | # ok! 44 | ``` 45 | 46 | Add `--current-size` flag to show the current size your image: 47 | 48 | ```bash 49 | $ disl your-image-name:label 300MiB --current-size 50 | your-image-name:label size is 414.4 MiB 51 | your-image-name:label exceeds 300MiB limit by 114.4 MiB 52 | ``` 53 | 54 | 55 | Add `--exit-zero` flag to force the exit code to be 0 even if there are errors: 56 | 57 | ```bash 58 | $ disl your-image-name:label 300MiB --exit-zero 59 | your-image-name:label exceeds 300MiB limit by 114.4 MiB 60 | 61 | $ echo $? 62 | 0 63 | ``` 64 | 65 | You can combine all flags together: 66 | 67 | ```bash 68 | $ disl your-image-name:label 300MiB --max-layers=5 --current-size --exit-zero 69 | your-image-name:label size is 414.4 MiB 70 | your-image-name:label exceeds 300MiB limit by 114.4 MiB 71 | your-image-name:label exceeds 5 maximum layers by 2 72 | ``` 73 | 74 | Run `disl` as a module: 75 | 76 | ```bash 77 | $ python -m docker_image_size_limit your-image-name:label 300MiB 78 | your-image-name:label exceeds 300MiB limit by 114.4 MiB 79 | ``` 80 | 81 | 82 | 83 | ## Options 84 | 85 | You can specify your image as: 86 | 87 | - Image name: `python` 88 | - Image name with tag: `python:3.6.6-alpine` 89 | 90 | You can specify your size as: 91 | 92 | - Raw number of bytes: `1024` 93 | - Human-readable megabytes: `30 MB` or `30 MiB` 94 | - Human-readable gigabytes: `1 GB` or `1 GiB` 95 | - Any other size supported by [`humanfriendly`](https://humanfriendly.readthedocs.io/en/latest/api.html#humanfriendly.parse_size) 96 | 97 | 98 | ## Programmatic usage 99 | 100 | You can also import and use this library as `python` code: 101 | 102 | ```python 103 | from docker import from_env 104 | from docker_image_size_limit import check_image_size 105 | 106 | oversize = check_image_size(from_env(), 'image-name:latest', '1 GiB') 107 | assert oversize < 0, 'Too big image!' # negative oversize - is a good thing! 108 | ``` 109 | 110 | We also ship [PEP-561](https://www.python.org/dev/peps/pep-0561/) 111 | compatible type annotations with this library. 112 | 113 | 114 | ## GitHub Action 115 | 116 | You can also use this check as a [GitHub Action](https://github.com/marketplace/actions/docker-image-size-limit): 117 | 118 | ```yaml 119 | - uses: wemake-services/docker-image-size-limit@master 120 | with: 121 | image: "$YOUR_IMAGE_NAME" 122 | size: "$YOUR_SIZE_LIMIT" 123 | # optional fields: 124 | max_layers: 5 125 | show_current_size: false 126 | exit_zero: false 127 | ``` 128 | 129 | Here's [an example](https://github.com/wemake-services/docker-image-size-limit/actions?query=workflow%3Adisl). 130 | 131 | 132 | ## Docker Image 133 | 134 | We have a [pre-built image](https://hub.docker.com/r/wemakeservices/docker-image-size-limit) available. 135 | 136 | First, pull our pre-built docker image: 137 | 138 | ```bash 139 | docker pull wemakeservices/docker-image-size-limit 140 | ``` 141 | 142 | Then you can use it like so: 143 | 144 | ```bash 145 | docker run -v /var/run/docker.sock:/var/run/docker.sock --rm \ 146 | -e INPUT_IMAGE="$YOUR_IMAGE_NAME" \ 147 | -e INPUT_SIZE="$YOUR_SIZE_LIMIT" \ 148 | -e INPUT_MAX_LAYERS="$YOUR_MAX_LAYERS" \ 149 | -e INPUT_SHOW_CURRENT_SIZE="true" \ 150 | -e INPUT_EXIT_ZERO="true" \ 151 | wemakeservices/docker-image-size-limit 152 | ``` 153 | 154 | 155 | ## Should I use it? 156 | 157 | You can use this script instead: 158 | 159 | ```bash 160 | LIMIT=1024 # adjust at your will 161 | IMAGE='your-image-name:latest' 162 | 163 | SIZE="$(docker image inspect "$IMAGE" --format='{{.Size}}')" 164 | test "$SIZE" -gt "$LIMIT" && echo 'Limit exceeded'; exit 1 || echo 'Ok!' 165 | ``` 166 | 167 | But I prefer to reuse tools over 168 | custom `bash` scripts here and there. 169 | 170 | 171 | ## License 172 | 173 | MIT. 174 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # This is a definition file for a Github Action. 2 | # See: https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-docker-container-action 3 | 4 | # We also define metadata here: 5 | # See: https://docs.github.com/en/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions 6 | 7 | name: 'docker-image-size-limit' 8 | description: 'Runs docker-image-size-limit as a GitHub Action' 9 | branding: 10 | icon: 'check' 11 | color: 'blue' 12 | 13 | inputs: 14 | image: 15 | description: 'Image name to be checked' 16 | required: true 17 | size: 18 | description: 'Human readable size as the hard limit' 19 | required: true 20 | max_layers: 21 | description: 'The maximum number of layers in the image' 22 | required: false 23 | default: -1 24 | show_current_size: 25 | description: 'Show the current size of the image' 26 | required: false 27 | default: 'false' 28 | exit_zero: 29 | description: 'Do not fail the action even if docker image size/layers exceed size and max_layers' 30 | required: false 31 | default: 'false' 32 | outputs: 33 | size: 34 | description: 'The output of docker-image-size-limit run' 35 | 36 | runs: 37 | using: 'docker' 38 | image: 'Dockerfile' 39 | args: 40 | - ${{ inputs.image }} 41 | - ${{ inputs.size }} 42 | - ${{ inputs.layers }} 43 | - ${{ inputs.show_current_size }} 44 | - ${{ inputs.exit_zero }} 45 | -------------------------------------------------------------------------------- /docker_image_size_limit/__init__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | from typing import List, NoReturn, Tuple 5 | 6 | import docker 7 | from docker.models.images import Image 8 | from humanfriendly import format_size, parse_size 9 | 10 | from docker_image_size_limit.version import get_version 11 | 12 | #: We use this variable to show version spec. 13 | _version = get_version( 14 | os.path.basename(os.path.dirname(__file__)), 15 | ) 16 | 17 | 18 | def main(prog_name: str = 'disl') -> NoReturn: # noqa: WPS210 19 | """Main CLI entrypoint.""" 20 | client = docker.from_env() 21 | arguments = _parse_args(prog_name=prog_name) 22 | extra_size, extra_layers, image_current_size = _check_image( 23 | client, 24 | image=arguments.image, 25 | max_size=arguments.max_size, 26 | max_layers=arguments.max_layers, 27 | ) 28 | if arguments.current_size: 29 | print('{0} current size is {1}'.format( # noqa: WPS421 30 | arguments.image, 31 | format_size(image_current_size, binary=True), 32 | )) 33 | 34 | exit_code = 0 35 | if extra_size > 0: 36 | print('{0} exceeds {1} limit by {2}'.format( # noqa: WPS421 37 | arguments.image, 38 | arguments.max_size, 39 | format_size(extra_size, binary=True), 40 | )) 41 | exit_code = 1 42 | if extra_layers > 0: 43 | print('{0} exceeds {1} maximum layers by {2}'.format( # noqa: WPS421 44 | arguments.image, 45 | arguments.max_layers, 46 | extra_layers, 47 | )) 48 | exit_code = 1 49 | if arguments.exit_zero: 50 | exit_code = 0 51 | sys.exit(exit_code) 52 | 53 | 54 | def _check_image( 55 | client: docker.DockerClient, 56 | image: str, 57 | max_size: str, 58 | max_layers: int, 59 | ) -> Tuple[int, int, int]: 60 | image_info = client.images.get(image) 61 | image_current_size: int = image_info.attrs['Size'] 62 | size_overflow = check_image_size(image_info, limit=max_size) 63 | if max_layers > 0: 64 | layers_overflow = check_image_layers(image_info, limit=max_layers) 65 | else: 66 | layers_overflow = 0 67 | return size_overflow, layers_overflow, image_current_size 68 | 69 | 70 | def check_image_size( 71 | image: Image, 72 | limit: str, 73 | ) -> int: 74 | """ 75 | Checks the image size of given image name. 76 | 77 | Compares it to the given size in bytes or in human readable format. 78 | 79 | Args: 80 | image: image object from docker. 81 | limit: human-readable size limit. 82 | 83 | Returns: 84 | Tresshold overflow in bytes. 85 | Can be negative in case ``image_limit`` is bigger 86 | than the actual image. We only care for values ``> 0``. 87 | 88 | """ 89 | image_size: int = image.attrs['Size'] 90 | 91 | try: 92 | image_limit = int(limit) 93 | except ValueError: 94 | image_limit = parse_size(limit, binary=True) 95 | 96 | return image_size - image_limit 97 | 98 | 99 | def check_image_layers( 100 | image: Image, 101 | limit: int, 102 | ) -> int: 103 | """ 104 | Checks the number of layers in an image. 105 | 106 | Args: 107 | image: image object from docker. 108 | limit: maximum number of layers. 109 | 110 | Returns: 111 | Tresshold overflow in number of layers. 112 | 113 | """ 114 | layers: List[str] = image.attrs['RootFS']['Layers'] 115 | return len(layers) - limit 116 | 117 | 118 | def _parse_args(prog_name: str) -> argparse.Namespace: 119 | parser = argparse.ArgumentParser( 120 | description='Keep your docker images small', 121 | prog=prog_name, 122 | ) 123 | parser.add_argument( 124 | '--version', action='version', version=_version, 125 | ) 126 | parser.add_argument( 127 | 'image', type=str, help='Docker image name to be checked', 128 | ) 129 | parser.add_argument( 130 | 'max_size', type=str, help='Human-readable size limit: 102 MB, 1GB', 131 | ) 132 | parser.add_argument( 133 | '--max-layers', 134 | type=int, 135 | help='Maximum number of image layers', 136 | default=-1, 137 | ) 138 | parser.add_argument( 139 | '--current-size', 140 | action='store_true', 141 | help='Display the current size of the Docker image', 142 | default=False, 143 | dest='current_size', 144 | ) 145 | parser.add_argument( 146 | '--exit-zero', 147 | action='store_true', 148 | help='Exit with 0 even if docker image size/layers exceed max_size and max-layers', # noqa: E501 149 | default=False, 150 | dest='exit_zero', 151 | ) 152 | return parser.parse_args() 153 | -------------------------------------------------------------------------------- /docker_image_size_limit/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os.path import basename 3 | 4 | from docker_image_size_limit import main 5 | 6 | if __name__ == '__main__': # pragma: no cover 7 | interpreter_binary_name = basename(sys.executable) 8 | main( 9 | prog_name='{0} -m docker_image_size_limit'.format( 10 | interpreter_binary_name, 11 | ), 12 | ) 13 | -------------------------------------------------------------------------------- /docker_image_size_limit/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wemake-services/docker-image-size-limit/adcc4cd199260eff78f409ba231079ba7656c8ed/docker_image_size_limit/py.typed -------------------------------------------------------------------------------- /docker_image_size_limit/version.py: -------------------------------------------------------------------------------- 1 | from importlib import metadata 2 | 3 | 4 | def get_version(distribution_name: str) -> str: 5 | """Our helper to get version of a package.""" 6 | return metadata.version(distribution_name) 7 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "astor" 5 | version = "0.8.1" 6 | description = "Read/rewrite/write Python ASTs" 7 | optional = false 8 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" 9 | groups = ["dev"] 10 | files = [ 11 | {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, 12 | {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, 13 | ] 14 | 15 | [[package]] 16 | name = "attrs" 17 | version = "24.2.0" 18 | description = "Classes Without Boilerplate" 19 | optional = false 20 | python-versions = ">=3.7" 21 | groups = ["dev"] 22 | files = [ 23 | {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, 24 | {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, 25 | ] 26 | 27 | [package.extras] 28 | benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] 29 | cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] 30 | dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] 31 | docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] 32 | tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\"", "pytest-xdist[psutil]"] 33 | tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.9\" and python_version < \"3.13\""] 34 | 35 | [[package]] 36 | name = "autorepr" 37 | version = "0.3.0" 38 | description = "Makes civilized __repr__, __str__, and __unicode__ methods" 39 | optional = false 40 | python-versions = "*" 41 | groups = ["dev"] 42 | files = [ 43 | {file = "autorepr-0.3.0-py2-none-any.whl", hash = "sha256:c34567e4073630feb52d9c788fc198085e9e9de4817e3b93b7c4c534fc689f11"}, 44 | {file = "autorepr-0.3.0-py2.py3-none-any.whl", hash = "sha256:1d9010d14fb325d3961e3aa73692685563f97d6ba4a2f0f735329fb37422599c"}, 45 | {file = "autorepr-0.3.0.tar.gz", hash = "sha256:ef770b84793d5433e6bb893054973b8c7ce6b487274f9c3f734f678cae11e85e"}, 46 | ] 47 | 48 | [[package]] 49 | name = "bandit" 50 | version = "1.7.10" 51 | description = "Security oriented static analyser for python code." 52 | optional = false 53 | python-versions = ">=3.8" 54 | groups = ["dev"] 55 | files = [ 56 | {file = "bandit-1.7.10-py3-none-any.whl", hash = "sha256:665721d7bebbb4485a339c55161ac0eedde27d51e638000d91c8c2d68343ad02"}, 57 | {file = "bandit-1.7.10.tar.gz", hash = "sha256:59ed5caf5d92b6ada4bf65bc6437feea4a9da1093384445fed4d472acc6cff7b"}, 58 | ] 59 | 60 | [package.dependencies] 61 | colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} 62 | PyYAML = ">=5.3.1" 63 | rich = "*" 64 | stevedore = ">=1.20.0" 65 | 66 | [package.extras] 67 | baseline = ["GitPython (>=3.1.30)"] 68 | sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] 69 | test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] 70 | toml = ["tomli (>=1.1.0) ; python_version < \"3.11\""] 71 | yaml = ["PyYAML"] 72 | 73 | [[package]] 74 | name = "cattrs" 75 | version = "24.1.2" 76 | description = "Composable complex class support for attrs and dataclasses." 77 | optional = false 78 | python-versions = ">=3.8" 79 | groups = ["dev"] 80 | files = [ 81 | {file = "cattrs-24.1.2-py3-none-any.whl", hash = "sha256:67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0"}, 82 | {file = "cattrs-24.1.2.tar.gz", hash = "sha256:8028cfe1ff5382df59dd36474a86e02d817b06eaf8af84555441bac915d2ef85"}, 83 | ] 84 | 85 | [package.dependencies] 86 | attrs = ">=23.1.0" 87 | exceptiongroup = {version = ">=1.1.1", markers = "python_version < \"3.11\""} 88 | typing-extensions = {version = ">=4.1.0,<4.6.3 || >4.6.3", markers = "python_version < \"3.11\""} 89 | 90 | [package.extras] 91 | bson = ["pymongo (>=4.4.0)"] 92 | cbor2 = ["cbor2 (>=5.4.6)"] 93 | msgpack = ["msgpack (>=1.0.5)"] 94 | msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] 95 | orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] 96 | pyyaml = ["pyyaml (>=6.0)"] 97 | tomlkit = ["tomlkit (>=0.11.8)"] 98 | ujson = ["ujson (>=5.7.0)"] 99 | 100 | [[package]] 101 | name = "certifi" 102 | version = "2024.8.30" 103 | description = "Python package for providing Mozilla's CA Bundle." 104 | optional = false 105 | python-versions = ">=3.6" 106 | groups = ["main", "dev"] 107 | files = [ 108 | {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, 109 | {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, 110 | ] 111 | 112 | [[package]] 113 | name = "charset-normalizer" 114 | version = "3.3.2" 115 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 116 | optional = false 117 | python-versions = ">=3.7.0" 118 | groups = ["main", "dev"] 119 | files = [ 120 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 121 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 122 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 123 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 124 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 125 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 126 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 127 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 128 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 129 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 130 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 131 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 132 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 133 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 134 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 135 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 136 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 137 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 138 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 139 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 140 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 141 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 142 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 143 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 144 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 145 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 146 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 147 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 148 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 149 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 150 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 151 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 152 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 153 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 154 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 155 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 156 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 157 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 158 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 159 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 160 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 161 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 162 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 163 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 164 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 165 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 166 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, 167 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, 168 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, 169 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, 170 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, 171 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, 172 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, 173 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, 174 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, 175 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, 176 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, 177 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, 178 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, 179 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 180 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 181 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 182 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 183 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 184 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 185 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 186 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 187 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 188 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 189 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 190 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 191 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 192 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 193 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 194 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 195 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 196 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 197 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 198 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 199 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 200 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 201 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 202 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 203 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 204 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 205 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 206 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 207 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 208 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 209 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 210 | ] 211 | 212 | [[package]] 213 | name = "click" 214 | version = "8.1.7" 215 | description = "Composable command line interface toolkit" 216 | optional = false 217 | python-versions = ">=3.7" 218 | groups = ["dev"] 219 | files = [ 220 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 221 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 222 | ] 223 | 224 | [package.dependencies] 225 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 226 | 227 | [[package]] 228 | name = "colorama" 229 | version = "0.4.6" 230 | description = "Cross-platform colored terminal text." 231 | optional = false 232 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 233 | groups = ["dev"] 234 | markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" 235 | files = [ 236 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 237 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 238 | ] 239 | 240 | [[package]] 241 | name = "configupdater" 242 | version = "3.2" 243 | description = "Parser like ConfigParser but for updating configuration files" 244 | optional = false 245 | python-versions = ">=3.6" 246 | groups = ["dev"] 247 | files = [ 248 | {file = "ConfigUpdater-3.2-py2.py3-none-any.whl", hash = "sha256:0f65a041627d7693840b4dd743581db4c441c97195298a29d075f91b79539df2"}, 249 | {file = "ConfigUpdater-3.2.tar.gz", hash = "sha256:9fdac53831c1b062929bf398b649b87ca30e7f1a735f3fbf482072804106306b"}, 250 | ] 251 | 252 | [package.extras] 253 | testing = ["flake8", "pytest", "pytest-cov", "pytest-randomly", "pytest-xdist", "sphinx"] 254 | 255 | [[package]] 256 | name = "coverage" 257 | version = "7.6.1" 258 | description = "Code coverage measurement for Python" 259 | optional = false 260 | python-versions = ">=3.8" 261 | groups = ["dev"] 262 | files = [ 263 | {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, 264 | {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, 265 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, 266 | {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, 267 | {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"}, 268 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, 269 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, 270 | {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, 271 | {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, 272 | {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, 273 | {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, 274 | {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, 275 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, 276 | {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, 277 | {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"}, 278 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, 279 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, 280 | {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, 281 | {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, 282 | {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, 283 | {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, 284 | {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, 285 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, 286 | {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, 287 | {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"}, 288 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, 289 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, 290 | {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, 291 | {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, 292 | {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, 293 | {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, 294 | {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, 295 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, 296 | {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, 297 | {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"}, 298 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, 299 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, 300 | {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, 301 | {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, 302 | {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, 303 | {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, 304 | {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, 305 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, 306 | {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, 307 | {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"}, 308 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, 309 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, 310 | {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, 311 | {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, 312 | {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, 313 | {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, 314 | {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, 315 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, 316 | {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, 317 | {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"}, 318 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, 319 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, 320 | {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, 321 | {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, 322 | {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, 323 | {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, 324 | {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, 325 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, 326 | {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, 327 | {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"}, 328 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, 329 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, 330 | {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, 331 | {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, 332 | {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, 333 | {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, 334 | {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, 335 | ] 336 | 337 | [package.dependencies] 338 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 339 | 340 | [package.extras] 341 | toml = ["tomli ; python_full_version <= \"3.11.0a6\""] 342 | 343 | [[package]] 344 | name = "darglint" 345 | version = "1.8.1" 346 | description = "A utility for ensuring Google-style docstrings stay up to date with the source code." 347 | optional = false 348 | python-versions = ">=3.6,<4.0" 349 | groups = ["dev"] 350 | files = [ 351 | {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, 352 | {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, 353 | ] 354 | 355 | [[package]] 356 | name = "dictdiffer" 357 | version = "0.9.0" 358 | description = "Dictdiffer is a library that helps you to diff and patch dictionaries." 359 | optional = false 360 | python-versions = "*" 361 | groups = ["dev"] 362 | files = [ 363 | {file = "dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595"}, 364 | {file = "dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578"}, 365 | ] 366 | 367 | [package.extras] 368 | all = ["Sphinx (>=3)", "check-manifest (>=0.42)", "mock (>=1.3.0)", "numpy (>=1.13.0) ; python_version < \"3.7\"", "numpy (>=1.15.0) ; python_version < \"3.8\"", "numpy (>=1.18.0) ; python_version < \"3.9\"", "numpy (>=1.20.0) ; python_version >= \"3.9\"", "pytest (==5.4.3) ; python_version <= \"3.5\"", "pytest (>=6) ; python_version > \"3.5\"", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "pytest-pycodestyle (>=2) ; python_version <= \"3.5\"", "pytest-pycodestyle (>=2.2.0) ; python_version > \"3.5\"", "pytest-pydocstyle (>=2) ; python_version <= \"3.5\"", "pytest-pydocstyle (>=2.2.0) ; python_version > \"3.5\"", "sphinx (>=3)", "sphinx-rtd-theme (>=0.2)", "tox (>=3.7.0)"] 369 | docs = ["Sphinx (>=3)", "sphinx-rtd-theme (>=0.2)"] 370 | numpy = ["numpy (>=1.13.0) ; python_version < \"3.7\"", "numpy (>=1.15.0) ; python_version < \"3.8\"", "numpy (>=1.18.0) ; python_version < \"3.9\"", "numpy (>=1.20.0) ; python_version >= \"3.9\""] 371 | tests = ["check-manifest (>=0.42)", "mock (>=1.3.0)", "pytest (==5.4.3) ; python_version <= \"3.5\"", "pytest (>=6) ; python_version > \"3.5\"", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "pytest-pycodestyle (>=2) ; python_version <= \"3.5\"", "pytest-pycodestyle (>=2.2.0) ; python_version > \"3.5\"", "pytest-pydocstyle (>=2) ; python_version <= \"3.5\"", "pytest-pydocstyle (>=2.2.0) ; python_version > \"3.5\"", "sphinx (>=3)", "tox (>=3.7.0)"] 372 | 373 | [[package]] 374 | name = "docker" 375 | version = "7.1.0" 376 | description = "A Python library for the Docker Engine API." 377 | optional = false 378 | python-versions = ">=3.8" 379 | groups = ["main"] 380 | files = [ 381 | {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, 382 | {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, 383 | ] 384 | 385 | [package.dependencies] 386 | pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} 387 | requests = ">=2.26.0" 388 | urllib3 = ">=1.26.0" 389 | 390 | [package.extras] 391 | dev = ["coverage (==7.2.7)", "pytest (==7.4.2)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.1.0)", "ruff (==0.1.8)"] 392 | docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] 393 | ssh = ["paramiko (>=2.4.3)"] 394 | websockets = ["websocket-client (>=1.3.0)"] 395 | 396 | [[package]] 397 | name = "docutils" 398 | version = "0.21.2" 399 | description = "Docutils -- Python Documentation Utilities" 400 | optional = false 401 | python-versions = ">=3.9" 402 | groups = ["dev"] 403 | files = [ 404 | {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, 405 | {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, 406 | ] 407 | 408 | [[package]] 409 | name = "dpath" 410 | version = "2.2.0" 411 | description = "Filesystem-like pathing and searching for dictionaries" 412 | optional = false 413 | python-versions = ">=3.7" 414 | groups = ["dev"] 415 | files = [ 416 | {file = "dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576"}, 417 | {file = "dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e"}, 418 | ] 419 | 420 | [[package]] 421 | name = "eradicate" 422 | version = "2.3.0" 423 | description = "Removes commented-out code." 424 | optional = false 425 | python-versions = "*" 426 | groups = ["dev"] 427 | files = [ 428 | {file = "eradicate-2.3.0-py3-none-any.whl", hash = "sha256:2b29b3dd27171f209e4ddd8204b70c02f0682ae95eecb353f10e8d72b149c63e"}, 429 | {file = "eradicate-2.3.0.tar.gz", hash = "sha256:06df115be3b87d0fc1c483db22a2ebb12bcf40585722810d809cc770f5031c37"}, 430 | ] 431 | 432 | [[package]] 433 | name = "exceptiongroup" 434 | version = "1.2.2" 435 | description = "Backport of PEP 654 (exception groups)" 436 | optional = false 437 | python-versions = ">=3.7" 438 | groups = ["dev"] 439 | markers = "python_version < \"3.11\"" 440 | files = [ 441 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 442 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 443 | ] 444 | 445 | [package.extras] 446 | test = ["pytest (>=6)"] 447 | 448 | [[package]] 449 | name = "flake8" 450 | version = "7.1.1" 451 | description = "the modular source code checker: pep8 pyflakes and co" 452 | optional = false 453 | python-versions = ">=3.8.1" 454 | groups = ["dev"] 455 | files = [ 456 | {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, 457 | {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, 458 | ] 459 | 460 | [package.dependencies] 461 | mccabe = ">=0.7.0,<0.8.0" 462 | pycodestyle = ">=2.12.0,<2.13.0" 463 | pyflakes = ">=3.2.0,<3.3.0" 464 | 465 | [[package]] 466 | name = "flake8-bandit" 467 | version = "4.1.1" 468 | description = "Automated security testing with bandit and flake8." 469 | optional = false 470 | python-versions = ">=3.6" 471 | groups = ["dev"] 472 | files = [ 473 | {file = "flake8_bandit-4.1.1-py3-none-any.whl", hash = "sha256:4c8a53eb48f23d4ef1e59293657181a3c989d0077c9952717e98a0eace43e06d"}, 474 | {file = "flake8_bandit-4.1.1.tar.gz", hash = "sha256:068e09287189cbfd7f986e92605adea2067630b75380c6b5733dab7d87f9a84e"}, 475 | ] 476 | 477 | [package.dependencies] 478 | bandit = ">=1.7.3" 479 | flake8 = ">=5.0.0" 480 | 481 | [[package]] 482 | name = "flake8-broken-line" 483 | version = "1.0.0" 484 | description = "Flake8 plugin to forbid backslashes for line breaks" 485 | optional = false 486 | python-versions = ">=3.8,<4.0" 487 | groups = ["dev"] 488 | files = [ 489 | {file = "flake8_broken_line-1.0.0-py3-none-any.whl", hash = "sha256:96c964336024a5030dc536a9f6fb02aa679e2d2a6b35b80a558b5136c35832a9"}, 490 | {file = "flake8_broken_line-1.0.0.tar.gz", hash = "sha256:e2c6a17f8d9a129e99c1320fce89b33843e2963871025c4c2bb7b8b8d8732a85"}, 491 | ] 492 | 493 | [package.dependencies] 494 | flake8 = ">5" 495 | 496 | [[package]] 497 | name = "flake8-bugbear" 498 | version = "24.8.19" 499 | description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." 500 | optional = false 501 | python-versions = ">=3.8.1" 502 | groups = ["dev"] 503 | files = [ 504 | {file = "flake8_bugbear-24.8.19-py3-none-any.whl", hash = "sha256:25bc3867f7338ee3b3e0916bf8b8a0b743f53a9a5175782ddc4325ed4f386b89"}, 505 | {file = "flake8_bugbear-24.8.19.tar.gz", hash = "sha256:9b77627eceda28c51c27af94560a72b5b2c97c016651bdce45d8f56c180d2d32"}, 506 | ] 507 | 508 | [package.dependencies] 509 | attrs = ">=19.2.0" 510 | flake8 = ">=6.0.0" 511 | 512 | [package.extras] 513 | dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] 514 | 515 | [[package]] 516 | name = "flake8-commas" 517 | version = "2.1.0" 518 | description = "Flake8 lint for trailing commas." 519 | optional = false 520 | python-versions = "*" 521 | groups = ["dev"] 522 | files = [ 523 | {file = "flake8-commas-2.1.0.tar.gz", hash = "sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263"}, 524 | {file = "flake8_commas-2.1.0-py2.py3-none-any.whl", hash = "sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54"}, 525 | ] 526 | 527 | [package.dependencies] 528 | flake8 = ">=2" 529 | 530 | [[package]] 531 | name = "flake8-comprehensions" 532 | version = "3.15.0" 533 | description = "A flake8 plugin to help you write better list/set/dict comprehensions." 534 | optional = false 535 | python-versions = ">=3.8" 536 | groups = ["dev"] 537 | files = [ 538 | {file = "flake8_comprehensions-3.15.0-py3-none-any.whl", hash = "sha256:b7e027bbb52be2ceb779ee12484cdeef52b0ad3c1fcb8846292bdb86d3034681"}, 539 | {file = "flake8_comprehensions-3.15.0.tar.gz", hash = "sha256:923c22603e0310376a6b55b03efebdc09753c69f2d977755cba8bb73458a5d4d"}, 540 | ] 541 | 542 | [package.dependencies] 543 | flake8 = ">=3,<3.2 || >3.2" 544 | 545 | [[package]] 546 | name = "flake8-debugger" 547 | version = "4.1.2" 548 | description = "ipdb/pdb statement checker plugin for flake8" 549 | optional = false 550 | python-versions = ">=3.7" 551 | groups = ["dev"] 552 | files = [ 553 | {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, 554 | {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, 555 | ] 556 | 557 | [package.dependencies] 558 | flake8 = ">=3.0" 559 | pycodestyle = "*" 560 | 561 | [[package]] 562 | name = "flake8-docstrings" 563 | version = "1.7.0" 564 | description = "Extension for flake8 which uses pydocstyle to check docstrings" 565 | optional = false 566 | python-versions = ">=3.7" 567 | groups = ["dev"] 568 | files = [ 569 | {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"}, 570 | {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"}, 571 | ] 572 | 573 | [package.dependencies] 574 | flake8 = ">=3" 575 | pydocstyle = ">=2.1" 576 | 577 | [[package]] 578 | name = "flake8-eradicate" 579 | version = "1.5.0" 580 | description = "Flake8 plugin to find commented out code" 581 | optional = false 582 | python-versions = ">=3.8,<4.0" 583 | groups = ["dev"] 584 | files = [ 585 | {file = "flake8_eradicate-1.5.0-py3-none-any.whl", hash = "sha256:18acc922ad7de623f5247c7d5595da068525ec5437dd53b22ec2259b96ce9d22"}, 586 | {file = "flake8_eradicate-1.5.0.tar.gz", hash = "sha256:aee636cb9ecb5594a7cd92d67ad73eb69909e5cc7bd81710cf9d00970f3983a6"}, 587 | ] 588 | 589 | [package.dependencies] 590 | attrs = "*" 591 | eradicate = ">=2.0,<3.0" 592 | flake8 = ">5" 593 | 594 | [[package]] 595 | name = "flake8-isort" 596 | version = "6.1.1" 597 | description = "flake8 plugin that integrates isort" 598 | optional = false 599 | python-versions = ">=3.8" 600 | groups = ["dev"] 601 | files = [ 602 | {file = "flake8_isort-6.1.1-py3-none-any.whl", hash = "sha256:0fec4dc3a15aefbdbe4012e51d5531a2eb5fa8b981cdfbc882296a59b54ede12"}, 603 | {file = "flake8_isort-6.1.1.tar.gz", hash = "sha256:c1f82f3cf06a80c13e1d09bfae460e9666255d5c780b859f19f8318d420370b3"}, 604 | ] 605 | 606 | [package.dependencies] 607 | flake8 = "*" 608 | isort = ">=5.0.0,<6" 609 | 610 | [package.extras] 611 | test = ["pytest"] 612 | 613 | [[package]] 614 | name = "flake8-plugin-utils" 615 | version = "1.3.3" 616 | description = "The package provides base classes and utils for flake8 plugin writing" 617 | optional = false 618 | python-versions = ">=3.6,<4.0" 619 | groups = ["dev"] 620 | files = [ 621 | {file = "flake8-plugin-utils-1.3.3.tar.gz", hash = "sha256:39f6f338d038b301c6fd344b06f2e81e382b68fa03c0560dff0d9b1791a11a2c"}, 622 | {file = "flake8_plugin_utils-1.3.3-py3-none-any.whl", hash = "sha256:e4848c57d9d50f19100c2d75fa794b72df068666a9041b4b0409be923356a3ed"}, 623 | ] 624 | 625 | [[package]] 626 | name = "flake8-pytest-style" 627 | version = "2.1.0" 628 | description = "A flake8 plugin checking common style issues or inconsistencies with pytest-based tests." 629 | optional = false 630 | python-versions = ">=3.9" 631 | groups = ["dev"] 632 | files = [ 633 | {file = "flake8_pytest_style-2.1.0-py3-none-any.whl", hash = "sha256:a0d6dddcd533bfc13f19b8445907be0330c5e6ccf7090bcd9d5fa5a0b1b65e71"}, 634 | {file = "flake8_pytest_style-2.1.0.tar.gz", hash = "sha256:fee6befdb5915d600ef24e38d48a077d0dcffb032945ae0169486e7ff8a1079a"}, 635 | ] 636 | 637 | [package.dependencies] 638 | flake8-plugin-utils = ">=1.3.2,<2.0.0" 639 | 640 | [[package]] 641 | name = "flake8-quotes" 642 | version = "3.4.0" 643 | description = "Flake8 lint for quotes." 644 | optional = false 645 | python-versions = "*" 646 | groups = ["dev"] 647 | files = [ 648 | {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, 649 | ] 650 | 651 | [package.dependencies] 652 | flake8 = "*" 653 | setuptools = "*" 654 | 655 | [[package]] 656 | name = "flake8-rst-docstrings" 657 | version = "0.3.0" 658 | description = "Python docstring reStructuredText (RST) validator for flake8" 659 | optional = false 660 | python-versions = ">=3.7" 661 | groups = ["dev"] 662 | files = [ 663 | {file = "flake8-rst-docstrings-0.3.0.tar.gz", hash = "sha256:d1ce22b4bd37b73cd86b8d980e946ef198cfcc18ed82fedb674ceaa2f8d1afa4"}, 664 | {file = "flake8_rst_docstrings-0.3.0-py3-none-any.whl", hash = "sha256:f8c3c6892ff402292651c31983a38da082480ad3ba253743de52989bdc84ca1c"}, 665 | ] 666 | 667 | [package.dependencies] 668 | flake8 = ">=3" 669 | pygments = "*" 670 | restructuredtext-lint = "*" 671 | 672 | [package.extras] 673 | develop = ["build", "twine"] 674 | 675 | [[package]] 676 | name = "flake8-string-format" 677 | version = "0.3.0" 678 | description = "string format checker, plugin for flake8" 679 | optional = false 680 | python-versions = "*" 681 | groups = ["dev"] 682 | files = [ 683 | {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, 684 | {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, 685 | ] 686 | 687 | [package.dependencies] 688 | flake8 = "*" 689 | 690 | [[package]] 691 | name = "flatten-dict" 692 | version = "0.4.2" 693 | description = "A flexible utility for flattening and unflattening dict-like objects in Python." 694 | optional = false 695 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 696 | groups = ["dev"] 697 | files = [ 698 | {file = "flatten-dict-0.4.2.tar.gz", hash = "sha256:506a96b6e6f805b81ae46a0f9f31290beb5fa79ded9d80dbe1b7fa236ab43076"}, 699 | {file = "flatten_dict-0.4.2-py2.py3-none-any.whl", hash = "sha256:7e245b20c4c718981212210eec4284a330c9f713e632e98765560e05421e48ad"}, 700 | ] 701 | 702 | [package.dependencies] 703 | six = ">=1.12,<2.0" 704 | 705 | [[package]] 706 | name = "furl" 707 | version = "2.1.3" 708 | description = "URL manipulation made simple." 709 | optional = false 710 | python-versions = "*" 711 | groups = ["dev"] 712 | files = [ 713 | {file = "furl-2.1.3-py2.py3-none-any.whl", hash = "sha256:9ab425062c4217f9802508e45feb4a83e54324273ac4b202f1850363309666c0"}, 714 | {file = "furl-2.1.3.tar.gz", hash = "sha256:5a6188fe2666c484a12159c18be97a1977a71d632ef5bb867ef15f54af39cc4e"}, 715 | ] 716 | 717 | [package.dependencies] 718 | orderedmultidict = ">=1.0.1" 719 | six = ">=1.8.0" 720 | 721 | [[package]] 722 | name = "gitignore-parser" 723 | version = "0.1.11" 724 | description = "A spec-compliant gitignore parser for Python 3.5+" 725 | optional = false 726 | python-versions = "*" 727 | groups = ["dev"] 728 | files = [ 729 | {file = "gitignore_parser-0.1.11.tar.gz", hash = "sha256:fa10fde48b44888eeefac096f53bcdad9b87a4ffd7db788558dbdf71ff3bc9db"}, 730 | ] 731 | 732 | [[package]] 733 | name = "humanfriendly" 734 | version = "10.0" 735 | description = "Human friendly output for text interfaces using Python" 736 | optional = false 737 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 738 | groups = ["main"] 739 | files = [ 740 | {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, 741 | {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, 742 | ] 743 | 744 | [package.dependencies] 745 | pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} 746 | 747 | [[package]] 748 | name = "identify" 749 | version = "2.6.1" 750 | description = "File identification library for Python" 751 | optional = false 752 | python-versions = ">=3.8" 753 | groups = ["dev"] 754 | files = [ 755 | {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, 756 | {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, 757 | ] 758 | 759 | [package.extras] 760 | license = ["ukkonen"] 761 | 762 | [[package]] 763 | name = "idna" 764 | version = "3.10" 765 | description = "Internationalized Domain Names in Applications (IDNA)" 766 | optional = false 767 | python-versions = ">=3.6" 768 | groups = ["main", "dev"] 769 | files = [ 770 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 771 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 772 | ] 773 | 774 | [package.extras] 775 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 776 | 777 | [[package]] 778 | name = "importlib-metadata" 779 | version = "8.5.0" 780 | description = "Read metadata from Python packages" 781 | optional = false 782 | python-versions = ">=3.8" 783 | groups = ["dev"] 784 | markers = "python_version < \"3.10\"" 785 | files = [ 786 | {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, 787 | {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, 788 | ] 789 | 790 | [package.dependencies] 791 | zipp = ">=3.20" 792 | 793 | [package.extras] 794 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 795 | cover = ["pytest-cov"] 796 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 797 | enabler = ["pytest-enabler (>=2.2)"] 798 | perf = ["ipython"] 799 | test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] 800 | type = ["pytest-mypy"] 801 | 802 | [[package]] 803 | name = "iniconfig" 804 | version = "2.0.0" 805 | description = "brain-dead simple config-ini parsing" 806 | optional = false 807 | python-versions = ">=3.7" 808 | groups = ["dev"] 809 | files = [ 810 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 811 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 812 | ] 813 | 814 | [[package]] 815 | name = "isort" 816 | version = "5.13.2" 817 | description = "A Python utility / library to sort Python imports." 818 | optional = false 819 | python-versions = ">=3.8.0" 820 | groups = ["dev"] 821 | files = [ 822 | {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, 823 | {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, 824 | ] 825 | 826 | [package.extras] 827 | colors = ["colorama (>=0.4.6)"] 828 | 829 | [[package]] 830 | name = "jmespath" 831 | version = "1.0.1" 832 | description = "JSON Matching Expressions" 833 | optional = false 834 | python-versions = ">=3.7" 835 | groups = ["dev"] 836 | files = [ 837 | {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, 838 | {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, 839 | ] 840 | 841 | [[package]] 842 | name = "loguru" 843 | version = "0.7.2" 844 | description = "Python logging made (stupidly) simple" 845 | optional = false 846 | python-versions = ">=3.5" 847 | groups = ["dev"] 848 | files = [ 849 | {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, 850 | {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, 851 | ] 852 | 853 | [package.dependencies] 854 | colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} 855 | win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} 856 | 857 | [package.extras] 858 | dev = ["Sphinx (==7.2.5) ; python_version >= \"3.9\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.2.2) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "mypy (==v1.5.1) ; python_version >= \"3.8\"", "pre-commit (==3.4.0) ; python_version >= \"3.8\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==7.4.0) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==4.1.0) ; python_version >= \"3.8\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.0.0) ; python_version >= \"3.8\"", "sphinx-autobuild (==2021.3.14) ; python_version >= \"3.9\"", "sphinx-rtd-theme (==1.3.0) ; python_version >= \"3.9\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.11.0) ; python_version >= \"3.8\""] 859 | 860 | [[package]] 861 | name = "markdown-it-py" 862 | version = "3.0.0" 863 | description = "Python port of markdown-it. Markdown parsing, done right!" 864 | optional = false 865 | python-versions = ">=3.8" 866 | groups = ["dev"] 867 | files = [ 868 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 869 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 870 | ] 871 | 872 | [package.dependencies] 873 | mdurl = ">=0.1,<1.0" 874 | 875 | [package.extras] 876 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 877 | code-style = ["pre-commit (>=3.0,<4.0)"] 878 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 879 | linkify = ["linkify-it-py (>=1,<3)"] 880 | plugins = ["mdit-py-plugins"] 881 | profiling = ["gprof2dot"] 882 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 883 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 884 | 885 | [[package]] 886 | name = "marshmallow" 887 | version = "3.22.0" 888 | description = "A lightweight library for converting complex datatypes to and from native Python datatypes." 889 | optional = false 890 | python-versions = ">=3.8" 891 | groups = ["dev"] 892 | files = [ 893 | {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, 894 | {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, 895 | ] 896 | 897 | [package.dependencies] 898 | packaging = ">=17.0" 899 | 900 | [package.extras] 901 | dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] 902 | docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] 903 | tests = ["pytest", "pytz", "simplejson"] 904 | 905 | [[package]] 906 | name = "marshmallow-polyfield" 907 | version = "5.11" 908 | description = "An unofficial extension to Marshmallow to allow for polymorphic fields" 909 | optional = false 910 | python-versions = ">=3.5" 911 | groups = ["dev"] 912 | files = [ 913 | {file = "marshmallow-polyfield-5.11.tar.gz", hash = "sha256:8075a9cc490da4af58b902b4a40a99882dd031adb7aaa96abd147a4fcd53415f"}, 914 | ] 915 | 916 | [package.dependencies] 917 | marshmallow = ">=3.0.0b10" 918 | 919 | [[package]] 920 | name = "mccabe" 921 | version = "0.7.0" 922 | description = "McCabe checker, plugin for flake8" 923 | optional = false 924 | python-versions = ">=3.6" 925 | groups = ["dev"] 926 | files = [ 927 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 928 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 929 | ] 930 | 931 | [[package]] 932 | name = "mdurl" 933 | version = "0.1.2" 934 | description = "Markdown URL utilities" 935 | optional = false 936 | python-versions = ">=3.7" 937 | groups = ["dev"] 938 | files = [ 939 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 940 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 941 | ] 942 | 943 | [[package]] 944 | name = "more-itertools" 945 | version = "10.5.0" 946 | description = "More routines for operating on iterables, beyond itertools" 947 | optional = false 948 | python-versions = ">=3.8" 949 | groups = ["dev"] 950 | files = [ 951 | {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, 952 | {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, 953 | ] 954 | 955 | [[package]] 956 | name = "mypy" 957 | version = "1.16.0" 958 | description = "Optional static typing for Python" 959 | optional = false 960 | python-versions = ">=3.9" 961 | groups = ["dev"] 962 | files = [ 963 | {file = "mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c"}, 964 | {file = "mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571"}, 965 | {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491"}, 966 | {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777"}, 967 | {file = "mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b"}, 968 | {file = "mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93"}, 969 | {file = "mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab"}, 970 | {file = "mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2"}, 971 | {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff"}, 972 | {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666"}, 973 | {file = "mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c"}, 974 | {file = "mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b"}, 975 | {file = "mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13"}, 976 | {file = "mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090"}, 977 | {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1"}, 978 | {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8"}, 979 | {file = "mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730"}, 980 | {file = "mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec"}, 981 | {file = "mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b"}, 982 | {file = "mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0"}, 983 | {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b"}, 984 | {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d"}, 985 | {file = "mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52"}, 986 | {file = "mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb"}, 987 | {file = "mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3"}, 988 | {file = "mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92"}, 989 | {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436"}, 990 | {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2"}, 991 | {file = "mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20"}, 992 | {file = "mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21"}, 993 | {file = "mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031"}, 994 | {file = "mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab"}, 995 | ] 996 | 997 | [package.dependencies] 998 | mypy_extensions = ">=1.0.0" 999 | pathspec = ">=0.9.0" 1000 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 1001 | typing_extensions = ">=4.6.0" 1002 | 1003 | [package.extras] 1004 | dmypy = ["psutil (>=4.0)"] 1005 | faster-cache = ["orjson"] 1006 | install-types = ["pip"] 1007 | mypyc = ["setuptools (>=50)"] 1008 | reports = ["lxml"] 1009 | 1010 | [[package]] 1011 | name = "mypy-extensions" 1012 | version = "1.0.0" 1013 | description = "Type system extensions for programs checked with the mypy type checker." 1014 | optional = false 1015 | python-versions = ">=3.5" 1016 | groups = ["dev"] 1017 | files = [ 1018 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 1019 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "nitpick" 1024 | version = "0.35.0" 1025 | description = "Enforce the same settings across multiple language-independent projects" 1026 | optional = false 1027 | python-versions = ">=3.8,<4.0" 1028 | groups = ["dev"] 1029 | files = [ 1030 | {file = "nitpick-0.35.0-py3-none-any.whl", hash = "sha256:9911d32c2d488b41914aa1a6d230531fa92bbe3d6610e4a468a5cb5e30fab907"}, 1031 | {file = "nitpick-0.35.0.tar.gz", hash = "sha256:098167a4c65655aca52c0ea3876b1e71cf634a27d0e17b971bce9bfcc1f3febe"}, 1032 | ] 1033 | 1034 | [package.dependencies] 1035 | attrs = ">=20.1.0" 1036 | autorepr = "*" 1037 | click = "*" 1038 | ConfigUpdater = "*" 1039 | dictdiffer = "*" 1040 | dpath = "*" 1041 | flake8 = ">=3.0.0" 1042 | flatten-dict = "*" 1043 | furl = "*" 1044 | gitignore_parser = "*" 1045 | identify = "*" 1046 | jmespath = "*" 1047 | loguru = "*" 1048 | marshmallow = ">=3.0.0b10" 1049 | marshmallow-polyfield = ">=5.10,<6.0" 1050 | more-itertools = "*" 1051 | packaging = "*" 1052 | pluggy = "*" 1053 | python-slugify = "*" 1054 | requests = "*" 1055 | requests-cache = ">=1.0.0" 1056 | "ruamel.yaml" = "*" 1057 | sortedcontainers = "*" 1058 | StrEnum = "*" 1059 | toml = "*" 1060 | tomlkit = ">=0.8.0" 1061 | 1062 | [package.extras] 1063 | doc = ["sphinx", "sphinx-gitref", "sphinx_rtd_theme", "sphobjinv"] 1064 | lint = ["pylint"] 1065 | test = ["freezegun", "pytest", "pytest-cov", "pytest-datadir", "pytest-socket", "pytest-testmon", "pytest-watch", "responses", "testfixtures"] 1066 | 1067 | [[package]] 1068 | name = "orderedmultidict" 1069 | version = "1.0.1" 1070 | description = "Ordered Multivalue Dictionary" 1071 | optional = false 1072 | python-versions = "*" 1073 | groups = ["dev"] 1074 | files = [ 1075 | {file = "orderedmultidict-1.0.1-py2.py3-none-any.whl", hash = "sha256:43c839a17ee3cdd62234c47deca1a8508a3f2ca1d0678a3bf791c87cf84adbf3"}, 1076 | {file = "orderedmultidict-1.0.1.tar.gz", hash = "sha256:04070bbb5e87291cc9bfa51df413677faf2141c73c61d2a5f7b26bea3cd882ad"}, 1077 | ] 1078 | 1079 | [package.dependencies] 1080 | six = ">=1.8.0" 1081 | 1082 | [[package]] 1083 | name = "packaging" 1084 | version = "24.1" 1085 | description = "Core utilities for Python packages" 1086 | optional = false 1087 | python-versions = ">=3.8" 1088 | groups = ["dev"] 1089 | files = [ 1090 | {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, 1091 | {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "pathspec" 1096 | version = "0.12.1" 1097 | description = "Utility library for gitignore style pattern matching of file paths." 1098 | optional = false 1099 | python-versions = ">=3.8" 1100 | groups = ["dev"] 1101 | files = [ 1102 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 1103 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 1104 | ] 1105 | 1106 | [[package]] 1107 | name = "pbr" 1108 | version = "6.1.0" 1109 | description = "Python Build Reasonableness" 1110 | optional = false 1111 | python-versions = ">=2.6" 1112 | groups = ["dev"] 1113 | files = [ 1114 | {file = "pbr-6.1.0-py2.py3-none-any.whl", hash = "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a"}, 1115 | {file = "pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24"}, 1116 | ] 1117 | 1118 | [[package]] 1119 | name = "pep8-naming" 1120 | version = "0.13.3" 1121 | description = "Check PEP-8 naming conventions, plugin for flake8" 1122 | optional = false 1123 | python-versions = ">=3.7" 1124 | groups = ["dev"] 1125 | files = [ 1126 | {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, 1127 | {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, 1128 | ] 1129 | 1130 | [package.dependencies] 1131 | flake8 = ">=5.0.0" 1132 | 1133 | [[package]] 1134 | name = "platformdirs" 1135 | version = "4.3.6" 1136 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 1137 | optional = false 1138 | python-versions = ">=3.8" 1139 | groups = ["dev"] 1140 | files = [ 1141 | {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, 1142 | {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, 1143 | ] 1144 | 1145 | [package.extras] 1146 | docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] 1147 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] 1148 | type = ["mypy (>=1.11.2)"] 1149 | 1150 | [[package]] 1151 | name = "pluggy" 1152 | version = "1.5.0" 1153 | description = "plugin and hook calling mechanisms for python" 1154 | optional = false 1155 | python-versions = ">=3.8" 1156 | groups = ["dev"] 1157 | files = [ 1158 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 1159 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 1160 | ] 1161 | 1162 | [package.extras] 1163 | dev = ["pre-commit", "tox"] 1164 | testing = ["pytest", "pytest-benchmark"] 1165 | 1166 | [[package]] 1167 | name = "pycodestyle" 1168 | version = "2.12.1" 1169 | description = "Python style guide checker" 1170 | optional = false 1171 | python-versions = ">=3.8" 1172 | groups = ["dev"] 1173 | files = [ 1174 | {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, 1175 | {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "pydocstyle" 1180 | version = "6.3.0" 1181 | description = "Python docstring style checker" 1182 | optional = false 1183 | python-versions = ">=3.6" 1184 | groups = ["dev"] 1185 | files = [ 1186 | {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, 1187 | {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, 1188 | ] 1189 | 1190 | [package.dependencies] 1191 | snowballstemmer = ">=2.2.0" 1192 | 1193 | [package.extras] 1194 | toml = ["tomli (>=1.2.3) ; python_version < \"3.11\""] 1195 | 1196 | [[package]] 1197 | name = "pyflakes" 1198 | version = "3.2.0" 1199 | description = "passive checker of Python programs" 1200 | optional = false 1201 | python-versions = ">=3.8" 1202 | groups = ["dev"] 1203 | files = [ 1204 | {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, 1205 | {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "pygments" 1210 | version = "2.18.0" 1211 | description = "Pygments is a syntax highlighting package written in Python." 1212 | optional = false 1213 | python-versions = ">=3.8" 1214 | groups = ["dev"] 1215 | files = [ 1216 | {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, 1217 | {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, 1218 | ] 1219 | 1220 | [package.extras] 1221 | windows-terminal = ["colorama (>=0.4.6)"] 1222 | 1223 | [[package]] 1224 | name = "pyreadline3" 1225 | version = "3.5.4" 1226 | description = "A python implementation of GNU readline." 1227 | optional = false 1228 | python-versions = ">=3.8" 1229 | groups = ["main"] 1230 | markers = "sys_platform == \"win32\"" 1231 | files = [ 1232 | {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, 1233 | {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, 1234 | ] 1235 | 1236 | [package.extras] 1237 | dev = ["build", "flake8", "mypy", "pytest", "twine"] 1238 | 1239 | [[package]] 1240 | name = "pytest" 1241 | version = "8.4.0" 1242 | description = "pytest: simple powerful testing with Python" 1243 | optional = false 1244 | python-versions = ">=3.9" 1245 | groups = ["dev"] 1246 | files = [ 1247 | {file = "pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e"}, 1248 | {file = "pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6"}, 1249 | ] 1250 | 1251 | [package.dependencies] 1252 | colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} 1253 | exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} 1254 | iniconfig = ">=1" 1255 | packaging = ">=20" 1256 | pluggy = ">=1.5,<2" 1257 | pygments = ">=2.7.2" 1258 | tomli = {version = ">=1", markers = "python_version < \"3.11\""} 1259 | 1260 | [package.extras] 1261 | dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] 1262 | 1263 | [[package]] 1264 | name = "pytest-cov" 1265 | version = "6.1.1" 1266 | description = "Pytest plugin for measuring coverage." 1267 | optional = false 1268 | python-versions = ">=3.9" 1269 | groups = ["dev"] 1270 | files = [ 1271 | {file = "pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde"}, 1272 | {file = "pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a"}, 1273 | ] 1274 | 1275 | [package.dependencies] 1276 | coverage = {version = ">=7.5", extras = ["toml"]} 1277 | pytest = ">=4.6" 1278 | 1279 | [package.extras] 1280 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] 1281 | 1282 | [[package]] 1283 | name = "pytest-randomly" 1284 | version = "3.16.0" 1285 | description = "Pytest plugin to randomly order tests and control random.seed." 1286 | optional = false 1287 | python-versions = ">=3.9" 1288 | groups = ["dev"] 1289 | files = [ 1290 | {file = "pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6"}, 1291 | {file = "pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26"}, 1292 | ] 1293 | 1294 | [package.dependencies] 1295 | importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} 1296 | pytest = "*" 1297 | 1298 | [[package]] 1299 | name = "pytest-timeout" 1300 | version = "2.4.0" 1301 | description = "pytest plugin to abort hanging tests" 1302 | optional = false 1303 | python-versions = ">=3.7" 1304 | groups = ["dev"] 1305 | files = [ 1306 | {file = "pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2"}, 1307 | {file = "pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a"}, 1308 | ] 1309 | 1310 | [package.dependencies] 1311 | pytest = ">=7.0.0" 1312 | 1313 | [[package]] 1314 | name = "python-slugify" 1315 | version = "8.0.4" 1316 | description = "A Python slugify application that also handles Unicode" 1317 | optional = false 1318 | python-versions = ">=3.7" 1319 | groups = ["dev"] 1320 | files = [ 1321 | {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, 1322 | {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, 1323 | ] 1324 | 1325 | [package.dependencies] 1326 | text-unidecode = ">=1.3" 1327 | 1328 | [package.extras] 1329 | unidecode = ["Unidecode (>=1.1.1)"] 1330 | 1331 | [[package]] 1332 | name = "pywin32" 1333 | version = "306" 1334 | description = "Python for Window Extensions" 1335 | optional = false 1336 | python-versions = "*" 1337 | groups = ["main"] 1338 | markers = "sys_platform == \"win32\"" 1339 | files = [ 1340 | {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, 1341 | {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, 1342 | {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, 1343 | {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, 1344 | {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, 1345 | {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, 1346 | {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, 1347 | {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, 1348 | {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, 1349 | {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, 1350 | {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, 1351 | {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, 1352 | {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, 1353 | {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, 1354 | ] 1355 | 1356 | [[package]] 1357 | name = "pyyaml" 1358 | version = "6.0.2" 1359 | description = "YAML parser and emitter for Python" 1360 | optional = false 1361 | python-versions = ">=3.8" 1362 | groups = ["dev"] 1363 | files = [ 1364 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 1365 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 1366 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 1367 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 1368 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 1369 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 1370 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 1371 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 1372 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 1373 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 1374 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 1375 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 1376 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 1377 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 1378 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 1379 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 1380 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 1381 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 1382 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 1383 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 1384 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 1385 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 1386 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 1387 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 1388 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 1389 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 1390 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 1391 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 1392 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 1393 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 1394 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 1395 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 1396 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 1397 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 1398 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 1399 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 1400 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 1401 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 1402 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 1403 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 1404 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 1405 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 1406 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 1407 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 1408 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 1409 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 1410 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 1411 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 1412 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 1413 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 1414 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 1415 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 1416 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 1417 | ] 1418 | 1419 | [[package]] 1420 | name = "requests" 1421 | version = "2.32.3" 1422 | description = "Python HTTP for Humans." 1423 | optional = false 1424 | python-versions = ">=3.8" 1425 | groups = ["main", "dev"] 1426 | files = [ 1427 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, 1428 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, 1429 | ] 1430 | 1431 | [package.dependencies] 1432 | certifi = ">=2017.4.17" 1433 | charset-normalizer = ">=2,<4" 1434 | idna = ">=2.5,<4" 1435 | urllib3 = ">=1.21.1,<3" 1436 | 1437 | [package.extras] 1438 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 1439 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 1440 | 1441 | [[package]] 1442 | name = "requests-cache" 1443 | version = "1.2.1" 1444 | description = "A persistent cache for python requests" 1445 | optional = false 1446 | python-versions = ">=3.8" 1447 | groups = ["dev"] 1448 | files = [ 1449 | {file = "requests_cache-1.2.1-py3-none-any.whl", hash = "sha256:1285151cddf5331067baa82598afe2d47c7495a1334bfe7a7d329b43e9fd3603"}, 1450 | {file = "requests_cache-1.2.1.tar.gz", hash = "sha256:68abc986fdc5b8d0911318fbb5f7c80eebcd4d01bfacc6685ecf8876052511d1"}, 1451 | ] 1452 | 1453 | [package.dependencies] 1454 | attrs = ">=21.2" 1455 | cattrs = ">=22.2" 1456 | platformdirs = ">=2.5" 1457 | requests = ">=2.22" 1458 | url-normalize = ">=1.4" 1459 | urllib3 = ">=1.25.5" 1460 | 1461 | [package.extras] 1462 | all = ["boto3 (>=1.15)", "botocore (>=1.18)", "itsdangerous (>=2.0)", "pymongo (>=3)", "pyyaml (>=6.0.1)", "redis (>=3)", "ujson (>=5.4)"] 1463 | bson = ["bson (>=0.5)"] 1464 | docs = ["furo (>=2023.3,<2024.0)", "linkify-it-py (>=2.0,<3.0)", "myst-parser (>=1.0,<2.0)", "sphinx (>=5.0.2,<6.0.0)", "sphinx-autodoc-typehints (>=1.19)", "sphinx-automodapi (>=0.14)", "sphinx-copybutton (>=0.5)", "sphinx-design (>=0.2)", "sphinx-notfound-page (>=0.8)", "sphinxcontrib-apidoc (>=0.3)", "sphinxext-opengraph (>=0.9)"] 1465 | dynamodb = ["boto3 (>=1.15)", "botocore (>=1.18)"] 1466 | json = ["ujson (>=5.4)"] 1467 | mongodb = ["pymongo (>=3)"] 1468 | redis = ["redis (>=3)"] 1469 | security = ["itsdangerous (>=2.0)"] 1470 | yaml = ["pyyaml (>=6.0.1)"] 1471 | 1472 | [[package]] 1473 | name = "restructuredtext-lint" 1474 | version = "1.4.0" 1475 | description = "reStructuredText linter" 1476 | optional = false 1477 | python-versions = "*" 1478 | groups = ["dev"] 1479 | files = [ 1480 | {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, 1481 | ] 1482 | 1483 | [package.dependencies] 1484 | docutils = ">=0.11,<1.0" 1485 | 1486 | [[package]] 1487 | name = "rich" 1488 | version = "13.8.1" 1489 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 1490 | optional = false 1491 | python-versions = ">=3.7.0" 1492 | groups = ["dev"] 1493 | files = [ 1494 | {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, 1495 | {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, 1496 | ] 1497 | 1498 | [package.dependencies] 1499 | markdown-it-py = ">=2.2.0" 1500 | pygments = ">=2.13.0,<3.0.0" 1501 | 1502 | [package.extras] 1503 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 1504 | 1505 | [[package]] 1506 | name = "ruamel-yaml" 1507 | version = "0.18.6" 1508 | description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" 1509 | optional = false 1510 | python-versions = ">=3.7" 1511 | groups = ["dev"] 1512 | files = [ 1513 | {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, 1514 | {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, 1515 | ] 1516 | 1517 | [package.dependencies] 1518 | "ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} 1519 | 1520 | [package.extras] 1521 | docs = ["mercurial (>5.7)", "ryd"] 1522 | jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] 1523 | 1524 | [[package]] 1525 | name = "ruamel-yaml-clib" 1526 | version = "0.2.8" 1527 | description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" 1528 | optional = false 1529 | python-versions = ">=3.6" 1530 | groups = ["dev"] 1531 | markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\"" 1532 | files = [ 1533 | {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, 1534 | {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, 1535 | {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, 1536 | {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, 1537 | {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, 1538 | {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, 1539 | {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, 1540 | {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, 1541 | {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, 1542 | {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, 1543 | {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, 1544 | {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, 1545 | {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, 1546 | {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, 1547 | {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, 1548 | {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, 1549 | {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, 1550 | {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, 1551 | {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, 1552 | {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, 1553 | {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, 1554 | {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, 1555 | {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, 1556 | {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, 1557 | {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, 1558 | {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, 1559 | {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, 1560 | {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, 1561 | {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, 1562 | {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, 1563 | {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, 1564 | {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, 1565 | {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, 1566 | {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, 1567 | {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, 1568 | {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, 1569 | {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, 1570 | {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, 1571 | {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, 1572 | {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, 1573 | {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, 1574 | {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, 1575 | {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, 1576 | {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, 1577 | {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, 1578 | {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, 1579 | {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, 1580 | {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, 1581 | {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, 1582 | {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, 1583 | ] 1584 | 1585 | [[package]] 1586 | name = "setuptools" 1587 | version = "78.1.1" 1588 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 1589 | optional = false 1590 | python-versions = ">=3.9" 1591 | groups = ["dev"] 1592 | files = [ 1593 | {file = "setuptools-78.1.1-py3-none-any.whl", hash = "sha256:c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561"}, 1594 | {file = "setuptools-78.1.1.tar.gz", hash = "sha256:fcc17fd9cd898242f6b4adfaca46137a9edef687f43e6f78469692a5e70d851d"}, 1595 | ] 1596 | 1597 | [package.extras] 1598 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] 1599 | core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] 1600 | cover = ["pytest-cov"] 1601 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] 1602 | enabler = ["pytest-enabler (>=2.2)"] 1603 | test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] 1604 | type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] 1605 | 1606 | [[package]] 1607 | name = "six" 1608 | version = "1.16.0" 1609 | description = "Python 2 and 3 compatibility utilities" 1610 | optional = false 1611 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1612 | groups = ["dev"] 1613 | files = [ 1614 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1615 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1616 | ] 1617 | 1618 | [[package]] 1619 | name = "snowballstemmer" 1620 | version = "2.2.0" 1621 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." 1622 | optional = false 1623 | python-versions = "*" 1624 | groups = ["dev"] 1625 | files = [ 1626 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, 1627 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, 1628 | ] 1629 | 1630 | [[package]] 1631 | name = "sortedcontainers" 1632 | version = "2.4.0" 1633 | description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" 1634 | optional = false 1635 | python-versions = "*" 1636 | groups = ["dev"] 1637 | files = [ 1638 | {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, 1639 | {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, 1640 | ] 1641 | 1642 | [[package]] 1643 | name = "stevedore" 1644 | version = "5.3.0" 1645 | description = "Manage dynamic plugins for Python applications" 1646 | optional = false 1647 | python-versions = ">=3.8" 1648 | groups = ["dev"] 1649 | files = [ 1650 | {file = "stevedore-5.3.0-py3-none-any.whl", hash = "sha256:1efd34ca08f474dad08d9b19e934a22c68bb6fe416926479ba29e5013bcc8f78"}, 1651 | {file = "stevedore-5.3.0.tar.gz", hash = "sha256:9a64265f4060312828151c204efbe9b7a9852a0d9228756344dbc7e4023e375a"}, 1652 | ] 1653 | 1654 | [package.dependencies] 1655 | pbr = ">=2.0.0" 1656 | 1657 | [[package]] 1658 | name = "strenum" 1659 | version = "0.4.15" 1660 | description = "An Enum that inherits from str." 1661 | optional = false 1662 | python-versions = "*" 1663 | groups = ["dev"] 1664 | files = [ 1665 | {file = "StrEnum-0.4.15-py3-none-any.whl", hash = "sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659"}, 1666 | {file = "StrEnum-0.4.15.tar.gz", hash = "sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff"}, 1667 | ] 1668 | 1669 | [package.extras] 1670 | docs = ["myst-parser[linkify]", "sphinx", "sphinx-rtd-theme"] 1671 | release = ["twine"] 1672 | test = ["pylint", "pytest", "pytest-black", "pytest-cov", "pytest-pylint"] 1673 | 1674 | [[package]] 1675 | name = "text-unidecode" 1676 | version = "1.3" 1677 | description = "The most basic Text::Unidecode port" 1678 | optional = false 1679 | python-versions = "*" 1680 | groups = ["dev"] 1681 | files = [ 1682 | {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, 1683 | {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, 1684 | ] 1685 | 1686 | [[package]] 1687 | name = "toml" 1688 | version = "0.10.2" 1689 | description = "Python Library for Tom's Obvious, Minimal Language" 1690 | optional = false 1691 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 1692 | groups = ["dev"] 1693 | files = [ 1694 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1695 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1696 | ] 1697 | 1698 | [[package]] 1699 | name = "tomli" 1700 | version = "2.0.1" 1701 | description = "A lil' TOML parser" 1702 | optional = false 1703 | python-versions = ">=3.7" 1704 | groups = ["dev"] 1705 | markers = "python_full_version <= \"3.11.0a6\"" 1706 | files = [ 1707 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 1708 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 1709 | ] 1710 | 1711 | [[package]] 1712 | name = "tomlkit" 1713 | version = "0.13.2" 1714 | description = "Style preserving TOML library" 1715 | optional = false 1716 | python-versions = ">=3.8" 1717 | groups = ["dev"] 1718 | files = [ 1719 | {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, 1720 | {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, 1721 | ] 1722 | 1723 | [[package]] 1724 | name = "types-docker" 1725 | version = "7.1.0.20250523" 1726 | description = "Typing stubs for docker" 1727 | optional = false 1728 | python-versions = ">=3.9" 1729 | groups = ["dev"] 1730 | files = [ 1731 | {file = "types_docker-7.1.0.20250523-py3-none-any.whl", hash = "sha256:ce6276bec00be41d1b00f87e31d0d39dcd0811a44c18f06b0046def3ee22b96e"}, 1732 | {file = "types_docker-7.1.0.20250523.tar.gz", hash = "sha256:fd7a2dbc75cbf58170f2ae9ac31d6e810ead646a5b28c016698edb293d43d60d"}, 1733 | ] 1734 | 1735 | [package.dependencies] 1736 | types-requests = "*" 1737 | urllib3 = ">=2" 1738 | 1739 | [[package]] 1740 | name = "types-humanfriendly" 1741 | version = "10.0.1.20250319" 1742 | description = "Typing stubs for humanfriendly" 1743 | optional = false 1744 | python-versions = ">=3.9" 1745 | groups = ["dev"] 1746 | files = [ 1747 | {file = "types_humanfriendly-10.0.1.20250319-py3-none-any.whl", hash = "sha256:0fec93cc793b91309481ed4bb9bed178ffde0076026fb3ac1cc3299ad7ecbc46"}, 1748 | {file = "types_humanfriendly-10.0.1.20250319.tar.gz", hash = "sha256:452aa1abbfff88a44cd9985eb459555bb1eae0cdd7bc084269146bfdbf1ff31d"}, 1749 | ] 1750 | 1751 | [[package]] 1752 | name = "types-requests" 1753 | version = "2.32.0.20240914" 1754 | description = "Typing stubs for requests" 1755 | optional = false 1756 | python-versions = ">=3.8" 1757 | groups = ["dev"] 1758 | files = [ 1759 | {file = "types-requests-2.32.0.20240914.tar.gz", hash = "sha256:2850e178db3919d9bf809e434eef65ba49d0e7e33ac92d588f4a5e295fffd405"}, 1760 | {file = "types_requests-2.32.0.20240914-py3-none-any.whl", hash = "sha256:59c2f673eb55f32a99b2894faf6020e1a9f4a402ad0f192bfee0b64469054310"}, 1761 | ] 1762 | 1763 | [package.dependencies] 1764 | urllib3 = ">=2" 1765 | 1766 | [[package]] 1767 | name = "typing-extensions" 1768 | version = "4.12.2" 1769 | description = "Backported and Experimental Type Hints for Python 3.8+" 1770 | optional = false 1771 | python-versions = ">=3.8" 1772 | groups = ["dev"] 1773 | files = [ 1774 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, 1775 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, 1776 | ] 1777 | 1778 | [[package]] 1779 | name = "url-normalize" 1780 | version = "1.4.3" 1781 | description = "URL normalization for Python" 1782 | optional = false 1783 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 1784 | groups = ["dev"] 1785 | files = [ 1786 | {file = "url-normalize-1.4.3.tar.gz", hash = "sha256:d23d3a070ac52a67b83a1c59a0e68f8608d1cd538783b401bc9de2c0fac999b2"}, 1787 | {file = "url_normalize-1.4.3-py2.py3-none-any.whl", hash = "sha256:ec3c301f04e5bb676d333a7fa162fa977ad2ca04b7e652bfc9fac4e405728eed"}, 1788 | ] 1789 | 1790 | [package.dependencies] 1791 | six = "*" 1792 | 1793 | [[package]] 1794 | name = "urllib3" 1795 | version = "2.2.3" 1796 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1797 | optional = false 1798 | python-versions = ">=3.8" 1799 | groups = ["main", "dev"] 1800 | files = [ 1801 | {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, 1802 | {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, 1803 | ] 1804 | 1805 | [package.extras] 1806 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] 1807 | h2 = ["h2 (>=4,<5)"] 1808 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1809 | zstd = ["zstandard (>=0.18.0)"] 1810 | 1811 | [[package]] 1812 | name = "wemake-python-styleguide" 1813 | version = "0.19.2" 1814 | description = "The strictest and most opinionated python linter ever" 1815 | optional = false 1816 | python-versions = "<4.0,>=3.9" 1817 | groups = ["dev"] 1818 | files = [ 1819 | {file = "wemake_python_styleguide-0.19.2-py3-none-any.whl", hash = "sha256:d53205dbb629755026d853d15fb3ca03ebb2717c97de4198b5676b9bdc0663bd"}, 1820 | {file = "wemake_python_styleguide-0.19.2.tar.gz", hash = "sha256:850fe70e6d525fd37ac51778e552a121a489f1bd057184de96ffd74a09aef414"}, 1821 | ] 1822 | 1823 | [package.dependencies] 1824 | astor = ">=0.8,<0.9" 1825 | attrs = "*" 1826 | darglint = ">=1.2,<2.0" 1827 | flake8 = ">=7.0,<8.0" 1828 | flake8-bandit = ">=4.1,<5.0" 1829 | flake8-broken-line = ">=1.0,<2.0" 1830 | flake8-bugbear = ">=24.2,<25.0" 1831 | flake8-commas = ">=2.0,<3.0" 1832 | flake8-comprehensions = ">=3.1,<4.0" 1833 | flake8-debugger = ">=4.0,<5.0" 1834 | flake8-docstrings = ">=1.3,<2.0" 1835 | flake8-eradicate = ">=1.5,<2.0" 1836 | flake8-isort = ">=6.0,<7.0" 1837 | flake8-quotes = ">=3.0,<4.0" 1838 | flake8-rst-docstrings = ">=0.3,<0.4" 1839 | flake8-string-format = ">=0.3,<0.4" 1840 | pep8-naming = ">=0.13,<0.14" 1841 | pygments = ">=2.4,<3.0" 1842 | setuptools = "*" 1843 | typing_extensions = ">=4.0,<5.0" 1844 | 1845 | [[package]] 1846 | name = "win32-setctime" 1847 | version = "1.1.0" 1848 | description = "A small Python utility to set file creation time on Windows" 1849 | optional = false 1850 | python-versions = ">=3.5" 1851 | groups = ["dev"] 1852 | markers = "sys_platform == \"win32\"" 1853 | files = [ 1854 | {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, 1855 | {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, 1856 | ] 1857 | 1858 | [package.extras] 1859 | dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] 1860 | 1861 | [[package]] 1862 | name = "zipp" 1863 | version = "3.20.2" 1864 | description = "Backport of pathlib-compatible object wrapper for zip files" 1865 | optional = false 1866 | python-versions = ">=3.8" 1867 | groups = ["dev"] 1868 | markers = "python_version < \"3.10\"" 1869 | files = [ 1870 | {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, 1871 | {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, 1872 | ] 1873 | 1874 | [package.extras] 1875 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 1876 | cover = ["pytest-cov"] 1877 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 1878 | enabler = ["pytest-enabler (>=2.2)"] 1879 | test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] 1880 | type = ["pytest-mypy"] 1881 | 1882 | [metadata] 1883 | lock-version = "2.1" 1884 | python-versions = "^3.9" 1885 | content-hash = "834e93983bffeb3f1d932ba9cda4e85b9a9ed8371625df1fb128b51b414cd40f" 1886 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "docker-image-size-limit" 3 | version = "2.1.0" 4 | description = "" 5 | license = "MIT" 6 | authors = [ 7 | "Nikita Sobolev " 8 | ] 9 | 10 | readme = "README.md" 11 | 12 | repository = "https://github.com/wemake-services/docker-image-size-limit" 13 | 14 | keywords = [ 15 | "docker", 16 | "docker image", 17 | "size limit", 18 | "wemake.services", 19 | "code quality" 20 | ] 21 | 22 | classifiers = [ 23 | "Development Status :: 5 - Production/Stable", 24 | "Environment :: Console", 25 | "Intended Audience :: Developers", 26 | "Operating System :: OS Independent", 27 | "Topic :: Software Development :: Libraries :: Python Modules", 28 | "Topic :: Software Development :: Quality Assurance", 29 | "Typing :: Typed", 30 | ] 31 | 32 | [tool.poetry.urls] 33 | "Funding" = "https://github.com/sponsors/wemake-services" 34 | 35 | [tool.poetry.scripts] 36 | disl = "docker_image_size_limit:main" 37 | 38 | [tool.poetry.dependencies] 39 | python = "^3.9" 40 | 41 | docker = ">=3.7" 42 | humanfriendly = ">=4.18,<11.0" 43 | 44 | [tool.poetry.group.dev.dependencies] 45 | pytest-cov = "^6.1" 46 | pytest-randomly = "^3.16" 47 | pytest-timeout = "^2.4" 48 | pytest = "^8.4" 49 | 50 | mypy = "^1.16" 51 | types-humanfriendly = "^10.0" 52 | types-docker = "^7.1" 53 | 54 | wemake-python-styleguide = "^0.19" 55 | flake8-pytest-style = "^2.1" 56 | nitpick = "^0.35" 57 | 58 | 59 | [build-system] 60 | requires = ["poetry-core>=1.9.0"] 61 | build-backend = "poetry.core.masonry.api" 62 | 63 | 64 | [tool.nitpick] 65 | style = "https://raw.githubusercontent.com/wemake-services/wemake-python-styleguide/0.19.2/styles/nitpick-style-wemake.toml" 66 | -------------------------------------------------------------------------------- /scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Passed args from GitHub Actions: 4 | : "${INPUT_IMAGE:=$1}" # Required 5 | : "${INPUT_SIZE:=$2}" # Required 6 | : "${INPUT_MAX_LAYERS:=$3}" # Optional 7 | : "${INPUT_SHOW_CURRENT_SIZE:=$4}" # Optional 8 | : "${INPUT_EXIT_ZERO:=$5}" # Optional 9 | 10 | # Default values, needed because `Dockerfile` can be used directly: 11 | # These values must match ones in `action.yml`! 12 | : "${INPUT_MAX_LAYERS:=-1}" 13 | : "${INPUT_SHOW_CURRENT_SIZE:=false}" 14 | : "${INPUT_EXIT_ZERO:=false}" 15 | 16 | # Diagnostic output: 17 | echo "Using image: $INPUT_IMAGE" 18 | echo "Size limit: $INPUT_SIZE" 19 | echo "Max layers: $INPUT_MAX_LAYERS" 20 | echo "Show Current Size: $INPUT_SHOW_CURRENT_SIZE" 21 | echo "Exit Zero: $INPUT_EXIT_ZERO" 22 | echo 'disl --version:' 23 | disl --version 24 | echo '=================================' 25 | echo 26 | 27 | SHOW_CURRENT_SIZE_FLAG='' 28 | if [ "$INPUT_SHOW_CURRENT_SIZE" = 'true' ]; then 29 | SHOW_CURRENT_SIZE_FLAG='--current-size' 30 | fi 31 | 32 | EXIT_ZERO_FLAG='' 33 | if [ "$INPUT_EXIT_ZERO" = 'true' ]; then 34 | EXIT_ZERO_FLAG='--exit-zero' 35 | fi 36 | 37 | 38 | # Runs disl: 39 | output=$(disl "$INPUT_IMAGE" "$INPUT_SIZE" --max-layers="$INPUT_MAX_LAYERS" $SHOW_CURRENT_SIZE_FLAG $EXIT_ZERO_FLAG) 40 | status="$?" 41 | 42 | # Sets the output variable for Github Action API: 43 | # See: https://help.github.com/en/articles/development-tools-for-github-action 44 | echo "output=$output" >> $GITHUB_OUTPUT 45 | echo '================================' 46 | echo 47 | 48 | # Fail the build in case status code is not 0: 49 | if [ "$status" != 0 ]; then 50 | echo 'Failing with output:' 51 | echo "$output" 52 | echo "Process failed with the status code: $status" 53 | exit "$status" 54 | fi 55 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # All configuration for plugins and other utils is defined here. 2 | # Read more about `setup.cfg`: 3 | # https://docs.python.org/3/distutils/configfile.html 4 | 5 | [flake8] 6 | # Base flake8 configuration: 7 | # https://flake8.pycqa.org/en/latest/user/configuration.html 8 | format = wemake 9 | show-source = true 10 | statistics = false 11 | doctests = true 12 | 13 | # Plugins: 14 | max-complexity = 6 15 | max-line-length = 80 16 | 17 | # darglint configuration: 18 | # https://github.com/terrencepreilly/darglint 19 | strictness = long 20 | docstring-style = numpy 21 | 22 | # Self settings: 23 | i-control-code = false 24 | 25 | # Excluding some directories: 26 | exclude = 27 | .git 28 | __pycache__ 29 | .venv 30 | .eggs 31 | *.egg 32 | 33 | # Exclude some pydoctest checks globally: 34 | ignore = D100, D104, D401, W504, RST303, RST304, DAR103, DAR203 35 | 36 | per-file-ignores = 37 | # There are `assert`s in tests: 38 | tests/*.py: WPS442, S101, S404, S603, S607, WPS226 39 | 40 | 41 | [isort] 42 | # isort configuration: 43 | # https://github.com/timothycrosley/isort/wiki/isort-Settings 44 | profile = wemake 45 | 46 | 47 | [tool:pytest] 48 | # py.test options: 49 | norecursedirs = *.egg .eggs dist build docs .tox .git __pycache__ 50 | 51 | # https://pypi.org/project/pytest-timeout/ 52 | timeout = 15 53 | 54 | # Strict `@xfail` by default: 55 | xfail_strict = true 56 | 57 | # You will need to measure your tests speed with `-n auto` and without it, 58 | # so you can see whether it gives you any performance gain, or just gives 59 | # you an overhead. See `docs/template/development-process.rst`. 60 | addopts = 61 | --doctest-modules 62 | --strict-markers 63 | --strict-config 64 | --cov=docker_image_size_limit 65 | --cov-branch 66 | --cov-report=term-missing:skip-covered 67 | --cov-report=html 68 | --cov-report=xml 69 | --cov-fail-under=100 70 | 71 | 72 | [mypy] 73 | # The mypy configurations: http://bit.ly/2zEl9WI 74 | enable_error_code = 75 | truthy-bool, 76 | truthy-iterable, 77 | redundant-expr, 78 | unused-awaitable, 79 | ignore-without-code, 80 | possibly-undefined, 81 | redundant-self, 82 | 83 | ignore_missing_imports = true 84 | strict = true 85 | warn_unreachable = true 86 | 87 | [mypy-docker_image_size_limit.version] 88 | # We allow unused `ignore` comments, because we cannot sync it between versions: 89 | warn_unused_ignores = false 90 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import docker 2 | import pytest 3 | from docker.models.images import Image 4 | 5 | 6 | @pytest.fixture(scope='session') 7 | def docker_client() -> docker.DockerClient: 8 | """Creates docker client suitable for tests.""" 9 | return docker.from_env() 10 | 11 | 12 | @pytest.fixture(scope='session') 13 | def image_name() -> str: 14 | """Returns image name that is used in tests.""" 15 | return 'python:3.6.6-alpine' 16 | 17 | 18 | @pytest.fixture(scope='session', autouse=True) 19 | def _docker_pull_image( 20 | docker_client: docker.DockerClient, image_name: str, # noqa: WPS442 21 | ) -> None: 22 | """Pulls docker image from Docker Hub.""" 23 | docker_client.images.pull(image_name) 24 | 25 | 26 | @pytest.fixture 27 | def docker_image( 28 | docker_client: docker.DockerClient, 29 | image_name: str, 30 | ) -> Image: 31 | """Returns image object that is used in tests.""" 32 | return docker_client.images.get(image_name) 33 | -------------------------------------------------------------------------------- /tests/test_check_max_layers.py: -------------------------------------------------------------------------------- 1 | from docker.models.images import Image 2 | 3 | from docker_image_size_limit import check_image_layers 4 | 5 | 6 | def test_check_image_layers_overflow( 7 | docker_image: Image, 8 | ) -> None: 9 | """Checks image layers with overflow.""" 10 | overflow = check_image_layers(docker_image, 1) 11 | 12 | assert overflow > 0 13 | 14 | 15 | def test_check_image_layers_correct( 16 | docker_image: Image, 17 | ) -> None: 18 | """Checks image layers with overflow.""" 19 | overflow = check_image_layers(docker_image, 100) 20 | 21 | assert overflow < 0 22 | -------------------------------------------------------------------------------- /tests/test_check_size.py: -------------------------------------------------------------------------------- 1 | from docker.models.images import Image 2 | 3 | from docker_image_size_limit import check_image_size 4 | 5 | 6 | def test_check_size_binary_overflow( 7 | docker_image: Image, 8 | ) -> None: 9 | """Checks size with binary limit.""" 10 | overflow = check_image_size(docker_image, '1024') 11 | 12 | assert overflow > 0 13 | 14 | 15 | def test_check_size_binary_correct( 16 | docker_image: Image, 17 | ) -> None: 18 | """Checks size with binary limit.""" 19 | overflow = check_image_size(docker_image, '1073741824') 20 | 21 | assert overflow < 0 22 | 23 | 24 | def test_check_size_human_overflow( 25 | docker_image: Image, 26 | ) -> None: 27 | """Checks size with human readable limit.""" 28 | overflow = check_image_size(docker_image, '10MB') 29 | 30 | assert overflow > 0 31 | 32 | 33 | def test_check_size_human_correct( 34 | docker_image: Image, 35 | ) -> None: 36 | """Checks size with human readable limit.""" 37 | overflow = check_image_size(docker_image, '10 GiB') 38 | 39 | assert overflow < 0 40 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | from os.path import basename 4 | 5 | 6 | def test_disl_exceeds_limit(image_name: str) -> None: 7 | """Runs `disl` command with met limit.""" 8 | process = subprocess.Popen( 9 | [ 10 | 'disl', 11 | image_name, 12 | '5MB', 13 | ], 14 | stdout=subprocess.PIPE, 15 | stderr=subprocess.PIPE, 16 | universal_newlines=True, 17 | encoding='utf8', 18 | ) 19 | output, _ = process.communicate() 20 | 21 | assert process.returncode == 1 22 | assert 'limit by' in output 23 | assert image_name in output 24 | 25 | 26 | def test_disl_normal(image_name: str) -> None: 27 | """Runs `disl` command with unmet limit.""" 28 | process = subprocess.Popen( 29 | [ 30 | 'disl', 31 | image_name, 32 | '1 GiB', 33 | ], 34 | stdout=subprocess.PIPE, 35 | stderr=subprocess.PIPE, 36 | universal_newlines=True, 37 | encoding='utf8', 38 | ) 39 | 40 | assert process.returncode is None 41 | 42 | 43 | def test_disl_max_layers(image_name: str) -> None: 44 | """Runs `disl` command with unmet limit.""" 45 | process = subprocess.Popen( 46 | [ 47 | 'disl', 48 | image_name, 49 | '1 GiB', 50 | '--max-layers', 51 | '1', 52 | ], 53 | stdout=subprocess.PIPE, 54 | stderr=subprocess.PIPE, 55 | universal_newlines=True, 56 | encoding='utf8', 57 | ) 58 | output, _ = process.communicate() 59 | 60 | assert process.returncode == 1 61 | assert 'maximum layers by' in output 62 | assert image_name in output 63 | 64 | 65 | def test_disl_current_size(image_name: str) -> None: 66 | """Runs `disl` command with --current-size flag.""" 67 | process = subprocess.Popen( 68 | [ 69 | 'disl', 70 | image_name, 71 | '1 kb', 72 | '--current-size', 73 | ], 74 | stdout=subprocess.PIPE, 75 | stderr=subprocess.PIPE, 76 | universal_newlines=True, 77 | encoding='utf8', 78 | ) 79 | output, _ = process.communicate() 80 | 81 | assert process.returncode == 1 82 | assert 'current size is' in output 83 | assert image_name in output 84 | 85 | 86 | def test_disl_exit_zero(image_name: str) -> None: 87 | """Runs `disl` command with --exit-zero flag.""" 88 | process = subprocess.Popen( 89 | [ 90 | 'disl', 91 | image_name, 92 | '1 kb', 93 | '--current-size', 94 | '--exit-zero', 95 | ], 96 | stdout=subprocess.PIPE, 97 | stderr=subprocess.PIPE, 98 | universal_newlines=True, 99 | encoding='utf8', 100 | ) 101 | output, _ = process.communicate() 102 | 103 | assert process.returncode == 0 104 | assert 'current size is' in output 105 | assert image_name in output 106 | 107 | 108 | def test_docker_image_size_limit_as_module(image_name: str) -> None: 109 | """Runs `disl` command with --exit-zero flag.""" 110 | interpreter_binary_name = basename(sys.executable) 111 | process = subprocess.Popen( 112 | [ 113 | '{0}'.format(interpreter_binary_name), 114 | '-m', 115 | 'docker_image_size_limit', 116 | image_name, 117 | '1 kb', 118 | '--current-size', 119 | '--exit-zero', 120 | ], 121 | stdout=subprocess.PIPE, 122 | stderr=subprocess.PIPE, 123 | universal_newlines=True, 124 | encoding='utf8', 125 | ) 126 | output, _ = process.communicate() 127 | 128 | assert process.returncode == 0 129 | assert 'current size is' in output 130 | assert image_name in output 131 | 132 | 133 | def test_docker_image_size_limit_as_module_help_flag(image_name: str) -> None: # noqa: WPS118,E501 134 | """Runs `disl` command via it python module.""" 135 | interpreter_binary_name = basename(sys.executable) 136 | process = subprocess.Popen( 137 | [ 138 | '{0}'.format(interpreter_binary_name), 139 | '-m', 140 | 'docker_image_size_limit', 141 | '--help', 142 | ], 143 | stdout=subprocess.PIPE, 144 | stderr=subprocess.PIPE, 145 | universal_newlines=True, 146 | encoding='utf8', 147 | ) 148 | output, _ = process.communicate() 149 | 150 | assert process.returncode == 0 151 | assert 'docker_image_size_limit' in output 152 | -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | 5 | from docker_image_size_limit import main 6 | 7 | 8 | def test_main_overflow( 9 | image_name: str, 10 | monkeypatch: pytest.MonkeyPatch, 11 | ) -> None: 12 | """Checks size with binary limit.""" 13 | monkeypatch.setattr(sys, 'argv', ['', image_name, '1 MB']) 14 | with pytest.raises(SystemExit) as exit_value: 15 | main() 16 | assert exit_value.value.code == 1 # noqa: WPS441 17 | 18 | 19 | def test_main_correct( 20 | image_name: str, 21 | monkeypatch: pytest.MonkeyPatch, 22 | ) -> None: 23 | """Checks size with binary limit.""" 24 | monkeypatch.setattr(sys, 'argv', ['', image_name, '1 GB']) 25 | with pytest.raises(SystemExit) as exit_value: 26 | main() 27 | assert exit_value.value.code == 0 # noqa: WPS441 28 | -------------------------------------------------------------------------------- /tests/test_version.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def test_disl_version() -> None: 5 | """Runs `disl` command with `--version` option.""" 6 | process = subprocess.Popen( 7 | [ 8 | 'disl', 9 | '--version', 10 | ], 11 | stdout=subprocess.PIPE, 12 | stderr=subprocess.PIPE, 13 | universal_newlines=True, 14 | encoding='utf8', 15 | ) 16 | 17 | assert process.returncode is None 18 | --------------------------------------------------------------------------------