diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..4dfba63 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ], + "labels": [ + "dependencies" + ], + "gitIgnoredAuthors": [ + "66853113+pre-commit-ci[bot]@users.noreply.github.com" + ], + "enabled": true, + "enabledManagers": [ + "pep621", + "github-actions" + ], + "ignoreDeps": [] +} diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..0d4a013 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,20 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v4 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..0c9c087 --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,17 @@ +name: Linting + +on: [push, pull_request, workflow_dispatch] + +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Linting + uses: astral-sh/ruff-action@v3 + with: + args: check + - name: Check Formatting + uses: astral-sh/ruff-action@v3 + with: + args: format --check diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 0000000..f39cfb5 --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,24 @@ +name: Type Checking + +on: [push, pull_request, workflow_dispatch] + +jobs: + mypy: + # uncomment the line before to disable this job if needed. + # if: false + name: mypy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + + - name: Set up Python + run: uv python install 3.12 + + - name: Run mypy + run: uv run --all-extras mypy . --strict diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml deleted file mode 100644 index 09c5cd1..0000000 --- a/.github/workflows/pythonpackage.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Python package - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] - - steps: - - uses: actions/checkout@v1 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt -# - name: Lint with flake8 -# run: | -# # stop the build if there are Python syntax errors or undefined names -# flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics -# # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide -# flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with pytest - run: | - pytest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..994809c --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,44 @@ +name: Tests + +on: + push: + branches: ["master", "main"] + pull_request: + branches: ["master", "main"] + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + env: + SKIP_COVERAGE_UPLOAD: false + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + + - name: Set up Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} + + - name: Run tests + # For example, using `pytest` + run: uv run -p ${{ matrix.python-version }} pytest --cov-report=xml + + - name: Run codacy-coverage-reporter + env: + CODACY_CONFIGURED: ${{ secrets.CODACY_PROJECT_TOKEN }} + if: ${{ env.CODACY_CONFIGURED != ''}} + uses: codacy/codacy-coverage-reporter-action@v1 + continue-on-error: true + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: ./coverage.xml diff --git a/.gitignore b/.gitignore index fabc272..28e6afd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,11 @@ build *__pycache__* *.swp .tox +.coverage +.vscode/ +.changelog_generator.toml +.python-version +site/ +api-test.py +repopack-output.txt +.envrc diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..282cc8a --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,5 @@ +{ + "MD026": { + "punctuation": ".,;:。,;:!" + } +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b0d08bf --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,58 @@ +ci: + autofix_commit_msg: "[pre-commit.ci] auto fixes from pre-commit.com hooks" +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-yaml + args: [--unsafe] + - id: trailing-whitespace + - id: check-toml + - id: check-merge-conflict + - id: end-of-file-fixer + + - repo: https://github.com/renovatebot/pre-commit-hooks + rev: 39.109.0 + hooks: + - id: renovate-config-validator + files: ^renovate\.json$ + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.1 + hooks: + - id: ruff + name: "lint with ruff" + - id: ruff-format + name: "format with ruff" + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: "v1.14.1" # Use the sha / tag you want to point at + hooks: + - id: mypy + name: "run mypy" + additional_dependencies: + - pydantic + + - repo: https://github.com/astral-sh/uv-pre-commit + # uv version. + rev: 0.5.20 + hooks: + # Update the uv lockfile + - id: uv-lock + - id: uv-export + name: "Export dependencies to 'requirements.txt'" + args: + [ + "--no-hashes", + "--no-dev", + "--no-emit-project", + "--output-file=requirements.txt", + ] + - id: uv-export + name: "Export dev dependencies to 'requirements-dev.txt'" + args: + [ + "--no-hashes", + "--no-emit-project", + "--output-file=requirements-dev.txt", + ] diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c3853bd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +This is an auto-generated log of all the changes that have been made to the +project since release 0.6.0. + +For the changelog from 'lice2' which has been back-ported into this project, +please refer to the "OLD_CHANGELOG2.md" file in the repository. + +For the original lice changelog, please refer to the "OLD_CHANGELOG.md" file in +the repository. + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3b3e7ba --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at . +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8b6433b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,214 @@ + +# Contributing to + +Thank you for your interest in contributing to Lice! We welcome +all contributions, big or small. + +If you are not sure where to start, please take a look at the [open +issues](https://github.com/licenses/lice/issues). If you have an +idea for a new feature or would like to report a bug, please open a new issue. + +We also welcome contributions to the documentation. If you find any errors or +would like to suggest improvements, please open a new issue or submit a pull + +## Prerequisites + +- Since this is a [Python](https://www.python.org/) project, you will need to have +Python installed on your machine. You can download the latest version of Python +from the [official website](https://www.python.org/downloads/) or using your +Operating system's package manager. + +- I'd recommend using [pyenv](https://github.com/pyenv/pyenv) to manage your +Python installations, the +[pyenv-installer](https://github.com/pyenv/pyenv-installer) works for Linux and +Mac OS X. For Windows, you can use the +[pyenv-win](https://github.com/pyenv-win/pyenv-win) port. See +[here](https://github.com/pyenv-win/pyenv-win#installation ) for installation +instructions. If you use `uv` (recommended and described below), it can install +Python for you, much quicker and easier than using `pyenv`. + +- This project requires **Python 3.9** or higher. + +- We use [uv](https://docs.astral.sh/uv/) to manage our dependencies. You should +have this installed as well. You can install `uv` by following the instructions +on their [website](https://docs.astral.sh/uv/getting-started/installation/). + +`uv` can be used to actually install Python, even if you do not have it +installed locally (either by system, pyenv or similar). + +For example, to install Python 3.12 using `uv`, you can run the following command: + +```console +uv python install 3.12 +``` + +If you already have a Python version installed, uv will use this. + +## Getting Started + +Before you start contributing, please make sure you have read and understood our +[Code of +Conduct](https://github.com/licenses/lice/blob/main/CODE_OF_CONDUCT.md) and +[License](https://github.com/licenses/lice/blob/main/LICENSE). + +To get started, follow these steps: + +1. Fork the repository and clone it to your local machine. +2. Install the required dependencies (see [next section](#install-dependencies)). +3. Create a new branch for your changes: `git checkout -b my-new-feature`. +4. Make your changes and commit them: `git commit -am 'Add some feature'`. +5. Push your changes to your fork: `git push origin my-new-feature`. +6. Create a new pull request. + +## Install Dependencies + +Run the following command to install the required dependencies: + +```console +uv sync +``` + +The `.venv` folder is already in the `.gitignore` file so will not be committed +to the repository. This is where the virtual environment will be created. + +You then need to activate the virtual environment: + +```console +source .venv/bin/activate +``` + +From here you can start working on the project. If you are using an IDE such as +VSCode or PyCharm, you can set their Python interpreter setting to use +the virtual environment that has just been created. + +## Install Git Pre-Commit hooks + +Please do this if you are intending to submit a PR. It will check commits +locally before they are pushed up to the Repo. + +```console +$ pre-commit install +pre-commit installed at .git/hooks/pre-commit +``` + +This will ensure that all code meets the required linting standard before being +committed. + +## Run pre-commit manually + +You can run these checks manually on all staged files using the below command : + +```console +poe pre +``` + +## Testing + +We are using [pytest](https://docs.pytest.org/) for testing. Tests will +automatically be run when you submit a pull request. You can also run them +manually using the following command: + +```console +pytest +``` + +If you add any new features, please add tests for them. This will help us to +ensure that the code is working as expected and will prevent any regressions. + +## Changelog + +The changelog is automatically generated using +[github-changelog-md](https://changelog.seapagan.net), so please do not edit it +manually. + +For maintainers, there is a POE task that will run this and update the changelog +file. + +```console +poe changelog +``` + +You would also need to add a GitHub Personal Access Token to a local config file +as usual. See the section in that tools +[Documentation](https://changelog.seapagan.net/installation/#setup-a-github-pat) +for information. + +**However, you should NOT include a change to the `CHANGELOG.md` file in any +Pull Requests. This will be handled by the maintainers when a new release is +made**. Your GitHub username will be added to the changelog automatically beside +your PR. + +## Convenience Tasks + +There are a few other convenience tasks that can be run using the `poe` command. +These are defined in the `pyproject.toml` file. + +Each of these tasks can have extra options added which will be passed to the +underlying tool. + +Run **`mypy`** on the code base in strict mode: + +```console +poe mypy +``` + +Format the code using **`ruff format`**: + +```console +poe format +``` + +Lint the code using **`ruff check`**: + +```console +poe ruff +``` + +Run `ruff`, `mypy` and `format` at the same time: + +```console +poe lint +``` + +## Documentation Tasks + +These are to help with developing and updating the documentation. + +- `poe docs:serve` - Serve the MkDocs locally for testing and development +- `poe docs:serve:all` - Same as above, but opens to all interfaces so you can + view it on other devices on your network +- `poe docs:build` - Build the MkDocs site into the `dist` folder +- `poe docs:publish` - Publish the docs to your GitHub pages. **Note that only + those with write-access to this repo can do this**. + +## Guidelines + +Here are some guidelines to follow when contributing to Lice: + +- Do not update the version number in the `pyproject.toml` file. This will be + done by the maintainers when a new release is made. +- Follow the [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guide. The + pre-commit hooks will check for this. We are using + [Ruff](https://docs.astral.sh/ruff/) as both a linter and code formatter. +- Try to have no linting errors or warnings. The pre-commit hooks will check for + this also. +- [MyPy](https://mypy.readthedocs.io/en/stable/) is installed and we are using + type hints. Please try to add type hints to your code. If you see any areas of + the code that are missing type hints, please feel free to open a PR and add + them 😁! +- Write clear and concise commit messages. +- Write tests for your code. +- Make sure your code passes all tests before submitting a pull request. +- Document your code using + [docstrings](https://www.python.org/dev/peps/pep-0257/). +- Use [GitHub issues](https://github.com/licenses/lice/issues) + to report bugs or suggest new features. + +## Contact + +If you have any questions or need help with contributing, please contact me +**@seapagan** on GitHub. You can also use the [GitHub +Discussions](https://github.com/licenses/lice/discussions) +feature. + +Happy contributing! diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 95bd18f..0000000 --- a/ChangeLog +++ /dev/null @@ -1,23 +0,0 @@ -2013-08-23 Alessandro Cresto Miseroglio - * core.py Unicode fixed (python 2.7 and 3.3 tested) - -2013-08-23 Alessandro Cresto Miseroglio - * core.py File name for a source file was added. - main (parser.add_argument [135]) -f (ofile) argument added - -2013-08-22 Alessandro Cresto Miseroglio - * core.py (lice) Formatted output for source file programs - was added. - languages implemented "lua, py, c, cc, pl, rb, sh, txt" - load_file_template (template) If template is StringIO, unicode - is supported in native mode (both 2.7[maibe] and 3.3) - load_package_template (content) StringIO support added - content.decode("utf-8") was removed - main (parser.add_argument [133]) -l (language) argument added - [20..24] LANGS and LANG_CMT added. - - * test.py some tests was changed (maibe become unnecessary) - test_file_template [31] assertEqual changed in assertNotEqual - test_package_template [39] assertEqual changed in assertNotEqual - - diff --git a/LICENSE b/LICENSE index 93a7257..7a0fcd7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013, Jeremy Carbaugh +Copyright (c) 2013-2025, Jeremy Carbaugh, Grant Ramsay All rights reserved. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index d87e1d0..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include LICENSE README.rst lice/template-*.txt \ No newline at end of file diff --git a/OLD_CHANGELOG.md b/OLD_CHANGELOG.md new file mode 100644 index 0000000..01b9b99 --- /dev/null +++ b/OLD_CHANGELOG.md @@ -0,0 +1,125 @@ +# Changelog + +## Changelog from the original 'lice' project + +2013-08-23 Alessandro Cresto Miseroglio + +* core.py Unicode fixed (python 2.7 and 3.3 tested) + +2013-08-23 Alessandro Cresto Miseroglio + +* core.py File name for a source file was added. + main (parser.add_argument [135]) -f (ofile) argument added + +2013-08-22 Alessandro Cresto Miseroglio + +* core.py (lice) Formatted output for source file programs + was added. + languages implemented "lua, py, c, cc, pl, rb, sh, txt" + load_file_template (template) If template is StringIO, unicode + is supported in native mode (both 2.7[maibe] and 3.3) + load_package_template (content) StringIO support added + content.decode("utf-8") was removed + main (parser.add_argument [133]) -l (language) argument added + [20..24] LANGS and LANG_CMT added. + +* test.py some tests was changed (maibe become unnecessary) + test_file_template [31] assertEqual changed in assertNotEqual + test_package_template [39] assertEqual changed in assertNotEqual + +## Release notes from the original README + +**0.6** + +* Add PowerShell support (thanks to `danijeljw `_) +* Add Rust support (thanks to `alex179ohm `_) +* Bugfixes (thanks to `ganziqim `_) +* Added support for Python 3.7 and 3.8, removed support for Python 3.4 + +Tested against Python 2.7, 3.5, 3.6, 3.7, and 3.8. + +**0.5** + +* Add support for SCM alias for lisp-style comments (thanks to `ejmr `_) +* Additional support for WTFPL and GPL2 licenses (thanks to `ejmr `_) +* Support for Python 3.4 and 3.5 (thanks to `ejmr `_) + +**0.4** + +* Use ASCII instead of Unicode for templates (thanks to `tabletcorry `_) +* Add Academic Free License ("AFL") v. 3.0 (thanks to `brianray `_) +* Add ISC (thanks to `masklinn `_) +* Add tox support for testing (thanks to `lukaszb `_) +* Show defaults when listing template variables + +**0.3** + +* Generate source file headers for some liceneses +* Discover available licenses at runtime +* Use getpass module for retrieving username +* Better unicode support for Python 3 (thanks to `astagi `_) +* Add Creative Commons licenese (thanks to `rjnienaber `_) + +**0.2** + +* Add AGPL 3 license +* Add extra templates variables to GPL 2 and 3 + +**0.1** + +* Initial release + +## Generated CHANGELOG from the original project repository + +**Closed Issues** + +* LICENSE-2.0 ([#56](https://github.com/licenses/lice/issues/56)) by [jcarbaugh](https://github.com/jcarbaugh) +* Two bugs ([#48](https://github.com/licenses/lice/issues/48)) by [ganziqim](https://github.com/ganziqim) +* WTFPL copyright notice and source header ([#46](https://github.com/licenses/lice/issues/46)) by [ejmr](https://github.com/ejmr) +* Don't render the boilerplate in the license template. ([#44](https://github.com/licenses/lice/issues/44)) by [ejmr](https://github.com/ejmr) +* Upgrade Creative Commons License to version 4.0 ([#43](https://github.com/licenses/lice/issues/43)) by [rgaiacs](https://github.com/rgaiacs) +* Not supporting Python 3.4 ([#38](https://github.com/licenses/lice/issues/38)) by [jiegec](https://github.com/jiegec) +* Experimental support of license-templates as template source. ([#30](https://github.com/licenses/lice/issues/30)) by [ejmr](https://github.com/ejmr) +* Print list of available styles ([#29](https://github.com/licenses/lice/issues/29)) by [jcarbaugh](https://github.com/jcarbaugh) +* Infer language only on the basis of file extension ([#27](https://github.com/licenses/lice/issues/27)) by [Yonaba](https://github.com/Yonaba) +* Existing file ([#26](https://github.com/licenses/lice/issues/26)) by [ejmr](https://github.com/ejmr) +* TypeError under Python 3 ([#17](https://github.com/licenses/lice/issues/17)) by [jcarbaugh](https://github.com/jcarbaugh) +* Error thrown on "subprocess.check_output('git config --get user.name'.split())" ([#8](https://github.com/licenses/lice/issues/8)) by [jcarbaugh](https://github.com/jcarbaugh) +* Additional licenses e.g. Creative Commons, OSI-approved licenses. ([#7](https://github.com/licenses/lice/issues/7)) by [jcarbaugh](https://github.com/jcarbaugh) +* Use non-newlined license files ([#5](https://github.com/licenses/lice/issues/5)) by [jcarbaugh](https://github.com/jcarbaugh) +* In-file Apache/GNU/MPL headers ([#2](https://github.com/licenses/lice/issues/2)) by [jcarbaugh](https://github.com/jcarbaugh) +* Affero GPL ([#1](https://github.com/licenses/lice/issues/1)) by [jcarbaugh](https://github.com/jcarbaugh) + +**Merged Pull Requests** + +* Add PowerShell language ([#53](https://github.com/licenses/lice/pull/53)) by [danijeljw](https://github.com/danijeljw) +* Add rust programming language ([#52](https://github.com/licenses/lice/pull/52)) by [alex179ohm](https://github.com/alex179ohm) +* Fix: issue #48 ([#49](https://github.com/licenses/lice/pull/49)) by [ganziqim](https://github.com/ganziqim) +* [Feature] Add `scm` as an acceptable value for `--language` ([#45](https://github.com/licenses/lice/pull/45)) by [ejmr](https://github.com/ejmr) +* Pep8 check ([#42](https://github.com/licenses/lice/pull/42)) by [lord63](https://github.com/lord63) +* Add --licenses flag to list all licenses/vars ([#37](https://github.com/licenses/lice/pull/37)) by [relrod](https://github.com/relrod) +* More languages ([#36](https://github.com/licenses/lice/pull/36)) by [relrod](https://github.com/relrod) +* Fixed launch-time error ([#35](https://github.com/licenses/lice/pull/35)) by [smcquay](https://github.com/smcquay) +* Ignore .tox dir ([#34](https://github.com/licenses/lice/pull/34)) by [bsdlp](https://github.com/bsdlp) +* Removed default txt file extension ([#33](https://github.com/licenses/lice/pull/33)) by [alex179ohm](https://github.com/alex179ohm) +* Incorporating license-templates package support ([#32](https://github.com/licenses/lice/pull/32)) by [ghost](https://github.com/ghost) +* Resume ([#25](https://github.com/licenses/lice/pull/25)) by [alex179ohm](https://github.com/alex179ohm) +* Docs updated ([#24](https://github.com/licenses/lice/pull/24)) by [alex179ohm](https://github.com/alex179ohm) +* Tests fixed ([#23](https://github.com/licenses/lice/pull/23)) by [alex179ohm](https://github.com/alex179ohm) +* Langs ([#22](https://github.com/licenses/lice/pull/22)) by [alex179ohm](https://github.com/alex179ohm) +* Added source file languages output format and file output redirect ([#21](https://github.com/licenses/lice/pull/21)) by [alex179ohm](https://github.com/alex179ohm) +* Fixes to template-cc0. ([#20](https://github.com/licenses/lice/pull/20)) by [ghost](https://github.com/ghost) +* Fix runtime error when running with python 3 ([#19](https://github.com/licenses/lice/pull/19)) by [ISF](https://github.com/ISF) +* Added tox and updated setup.py ([#18](https://github.com/licenses/lice/pull/18)) by [lukaszb](https://github.com/lukaszb) +* ISC license ([#16](https://github.com/licenses/lice/pull/16)) by [masklinn](https://github.com/masklinn) +* AFL v. 3.0 ([#15](https://github.com/licenses/lice/pull/15)) by [brianray](https://github.com/brianray) +* Replace unicode license text with ASCII ([#14](https://github.com/licenses/lice/pull/14)) by [tabletcorry](https://github.com/tabletcorry) +* Added Creative Commons licences. ([#13](https://github.com/licenses/lice/pull/13)) by [rjnienaber](https://github.com/rjnienaber) +* Added python3 compatibility ([#12](https://github.com/licenses/lice/pull/12)) by [astagi](https://github.com/astagi) +* Added the zlib License ([#11](https://github.com/licenses/lice/pull/11)) by [astagi](https://github.com/astagi) +* Added the WTFPL License ([#10](https://github.com/licenses/lice/pull/10)) by [aaronbassett](https://github.com/aaronbassett) +* Failure on Windows when getting default user ([#6](https://github.com/licenses/lice/pull/6)) by [rjnienaber](https://github.com/rjnienaber) +* Previously discussed changes ([#3](https://github.com/licenses/lice/pull/3)) by [JNRowe](https://github.com/JNRowe) + +--- +*This changelog was generated using [github-changelog-md](http://changelog.seapagan.net/) by [Seapagan](https://github.com/seapagan)* diff --git a/OLD_CHANGELOG2.md b/OLD_CHANGELOG2.md new file mode 100644 index 0000000..2e62442 --- /dev/null +++ b/OLD_CHANGELOG2.md @@ -0,0 +1,145 @@ +# Changelog + +This is an auto-generated log of all the changes that have been made to the +project on the `seapagan/lice2` fork. That fork has now been merged into the +`licenses/lice` project and the additional release numbers ignored. Note that +any links in the below changelog are to the `lice2` fork and not the original. + +For the original changelog, please refer to the "OLD_CHANGELOG.md" file in the repository. + +NOTE: The version numbers in this log are from the 'lice2' fork of this project +which has now been merged into the 'lice' project which will be released as +version **0.7.0** + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.13.0](https://github.com/seapagan/lice2/releases/tag/0.13.0) (November 13, 2024) + +**New Features** + +- Migrate from poetry to uv for dependency and project management ([#72](https://github.com/seapagan/lice2/pull/72)) by [seapagan](https://github.com/seapagan) + +**Dependency Updates** + +- Bump ruff from 0.6.3 to 0.6.4 ([#47](https://github.com/seapagan/lice2/pull/47)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump pymarkdownlnt from 0.9.22 to 0.9.23 ([#46](https://github.com/seapagan/lice2/pull/46)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump cryptography from 43.0.0 to 43.0.1 ([#43](https://github.com/seapagan/lice2/pull/43)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump mkdocs-autorefs from 1.1.0 to 1.2.0 ([#42](https://github.com/seapagan/lice2/pull/42)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump ruff from 0.6.2 to 0.6.3 ([#41](https://github.com/seapagan/lice2/pull/41)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump mkdocs-material from 9.5.33 to 9.5.34 ([#40](https://github.com/seapagan/lice2/pull/40)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump mkdocs from 1.6.0 to 1.6.1 ([#39](https://github.com/seapagan/lice2/pull/39)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump poethepoet from 0.27.0 to 0.28.0 ([#38](https://github.com/seapagan/lice2/pull/38)) by [dependabot[bot]](https://github.com/apps/dependabot) + +[`Full Changelog`](https://github.com/seapagan/lice2/compare/0.12.0...0.13.0) | [`Diff`](https://github.com/seapagan/lice2/compare/0.12.0...0.13.0.diff) | [`Patch`](https://github.com/seapagan/lice2/compare/0.12.0...0.13.0.patch) + +## [0.12.0](https://github.com/seapagan/lice2/releases/tag/0.12.0) (September 02, 2024) + +**New Features** + +- Add some more licenses in common usage ([#34](https://github.com/seapagan/lice2/pull/34)) by [seapagan](https://github.com/seapagan) +- Add more language options ([#33](https://github.com/seapagan/lice2/pull/33)) by [seapagan](https://github.com/seapagan) +- Add an API so that lice can be used inside other applications ([#31](https://github.com/seapagan/lice2/pull/31)) by [seapagan](https://github.com/seapagan) +- Provide an optional JSON output, listing licenses and languages for easier integration into other tools like a GUI ([#30](https://github.com/seapagan/lice2/pull/30)) by [seapagan](https://github.com/seapagan) +- Add '--version' flag to the CLI ([#29](https://github.com/seapagan/lice2/pull/29)) by [seapagan](https://github.com/seapagan) + +**Testing** + +- Tweak some of the test layouts and contents ([#32](https://github.com/seapagan/lice2/pull/32)) by [seapagan](https://github.com/seapagan) + +**Refactoring** + +- Refactor some of the logic in the CLI ([#35](https://github.com/seapagan/lice2/pull/35)) by [seapagan](https://github.com/seapagan) + +[`Full Changelog`](https://github.com/seapagan/lice2/compare/0.11.0...0.12.0) | [`Diff`](https://github.com/seapagan/lice2/compare/0.11.0...0.12.0.diff) | [`Patch`](https://github.com/seapagan/lice2/compare/0.11.0...0.12.0.patch) + +## [0.11.0](https://github.com/seapagan/lice2/releases/tag/0.11.0) (August 28, 2024) + +**New Features** + +- Add option to copy directly to clipboard ([#27](https://github.com/seapagan/lice2/pull/27)) by [seapagan](https://github.com/seapagan) + +**Dependency Updates** + +- Bump rich from 13.7.1 to 13.8.0 ([#26](https://github.com/seapagan/lice2/pull/26)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump ruff from 0.6.1 to 0.6.2 ([#25](https://github.com/seapagan/lice2/pull/25)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump mypy from 1.11.1 to 1.11.2 ([#24](https://github.com/seapagan/lice2/pull/24)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump mkdocs-autorefs from 1.0.1 to 1.1.0 ([#23](https://github.com/seapagan/lice2/pull/23)) by [dependabot[bot]](https://github.com/apps/dependabot) +- Bump typer from 0.12.4 to 0.12.5 ([#22](https://github.com/seapagan/lice2/pull/22)) by [dependabot[bot]](https://github.com/apps/dependabot) + +[`Full Changelog`](https://github.com/seapagan/lice2/compare/0.10.2...0.11.0) | [`Diff`](https://github.com/seapagan/lice2/compare/0.10.2...0.11.0.diff) | [`Patch`](https://github.com/seapagan/lice2/compare/0.10.2...0.11.0.patch) + +## [0.10.2](https://github.com/seapagan/lice2/releases/tag/0.10.2) (August 26, 2024) + +**Closed Issues** + +- Formatting issue with 'c', 'java' style and languages using the same comment template ([#17](https://github.com/seapagan/lice2/issues/17)) by [seapagan](https://github.com/seapagan) + +**New Features** + +- Add markdown mapping (same as HTML) ([#19](https://github.com/seapagan/lice2/pull/19)) by [seapagan](https://github.com/seapagan) + +**Bug Fixes** + +- Fix bug "formatting issue with 'c', 'java' style" ([#18](https://github.com/seapagan/lice2/pull/18)) by [seapagan](https://github.com/seapagan) + +[`Full Changelog`](https://github.com/seapagan/lice2/compare/0.10.1...0.10.2) | [`Diff`](https://github.com/seapagan/lice2/compare/0.10.1...0.10.2.diff) | [`Patch`](https://github.com/seapagan/lice2/compare/0.10.1...0.10.2.patch) + +## [0.10.1](https://github.com/seapagan/lice2/releases/tag/0.10.1) (August 26, 2024) + +Purely a documentation fix so that the updated README gets to PyPI + +[`Full Changelog`](https://github.com/seapagan/lice2/compare/0.10.0...0.10.1) | [`Diff`](https://github.com/seapagan/lice2/compare/0.10.0...0.10.1.diff) | [`Patch`](https://github.com/seapagan/lice2/compare/0.10.0...0.10.1.patch) + +## [0.10.0](https://github.com/seapagan/lice2/releases/tag/0.10.0) (August 26, 2024) + +**Bug Fixes** + +- Fix extra spaces and CR in output ([#14](https://github.com/seapagan/lice2/pull/14)) by [seapagan](https://github.com/seapagan) + +[`Full Changelog`](https://github.com/seapagan/lice2/compare/0.9.0...0.10.0) | [`Diff`](https://github.com/seapagan/lice2/compare/0.9.0...0.10.0.diff) | [`Patch`](https://github.com/seapagan/lice2/compare/0.9.0...0.10.0.patch) + +## [0.9.0](https://github.com/seapagan/lice2/releases/tag/0.9.0) (August 24, 2024) + +**New Features** + +- Convert CLI from 'argparse' to 'typer' ([#8](https://github.com/seapagan/lice2/pull/8)) by [seapagan](https://github.com/seapagan) +- Add config file functionality ([#7](https://github.com/seapagan/lice2/pull/7)) by [seapagan](https://github.com/seapagan) + +**Testing** + +- Improve test coverage ([#9](https://github.com/seapagan/lice2/pull/9)) by [seapagan](https://github.com/seapagan) + +**Documentation** + +- Create a docs website for the project ([#11](https://github.com/seapagan/lice2/pull/11)) by [seapagan](https://github.com/seapagan) + +[`Full Changelog`](https://github.com/seapagan/lice2/compare/0.8.0...0.9.0) | [`Diff`](https://github.com/seapagan/lice2/compare/0.8.0...0.9.0.diff) | [`Patch`](https://github.com/seapagan/lice2/compare/0.8.0...0.9.0.patch) + +## [0.8.0](https://github.com/seapagan/lice2/releases/tag/0.8.0) (August 19, 2024) + +**New Features** + +- Add prettier output for the listing of licenses and languages ([#4](https://github.com/seapagan/lice2/pull/4)) by [seapagan](https://github.com/seapagan) + +**Refactoring** + +- Major refactor into separate files ([#5](https://github.com/seapagan/lice2/pull/5)) by [seapagan](https://github.com/seapagan) +- Move templates into subfolder ([#3](https://github.com/seapagan/lice2/pull/3)) by [seapagan](https://github.com/seapagan) +- Refactor with strict ruff and mypy linting ([#1](https://github.com/seapagan/lice2/pull/1)) by [seapagan](https://github.com/seapagan) + +**Documentation** + +- Add new CHANGELOG and consolidate the old ones ([#6](https://github.com/seapagan/lice2/pull/6)) by [seapagan](https://github.com/seapagan) + +**Dependency Updates** + +- Bump pre-commit from 3.5.0 to 3.8.0 ([#2](https://github.com/seapagan/lice2/pull/2)) by [dependabot[bot]](https://github.com/apps/dependabot) + +[`Full Changelog`](https://github.com/seapagan/lice2/compare/0.7.0...0.8.0) | [`Diff`](https://github.com/seapagan/lice2/compare/0.7.0...0.8.0.diff) | [`Patch`](https://github.com/seapagan/lice2/compare/0.7.0...0.8.0.patch) + +## [0.7.0](https://github.com/seapagan/lice2/releases/tag/0.7.0) (August 18, 2024) + +This is the first release of this new fork. Main thing was getting the Python 3.12 compatibility fixed. + +--- +*This changelog was generated using [github-changelog-md](http://changelog.seapagan.net/) by [Seapagan](https://github.com/seapagan)* diff --git a/README.md b/README.md new file mode 100644 index 0000000..ce158c2 --- /dev/null +++ b/README.md @@ -0,0 +1,304 @@ +# lice + +Lice generates license files. No more hunting down licenses from other projects. + +- [Latest updates as of Jan 2025](#latest-updates-as-of-jan-2025) +- [Installation](#installation) + - [Development Version](#development-version) + - [Autocompletion](#autocompletion) +- [Overview](#overview) +- [I want XXXXXXXXX license in here!](#i-want-xxxxxxxxx-license-in-here) +- [Usage](#usage) +- [Config File](#config-file) +- [Integrate into your projects](#integrate-into-your-projects) +- [Integration with other tools](#integration-with-other-tools) +- [Contribute to the Development](#contribute-to-the-development) +- [Changelog](#changelog) + +## Latest updates as of Jan 2025 + +This version fixes the compatibility issue with Python 3.12, and adds some new +features: + +- It has an API that can be imported into your Python projects to allow you to + generate licenses directly from within your own project. +- Can read from a config file for default values. +- Can optionally copy the license to the clipboard automatically. +- Converted from 'argparse' to 'Typer' for CLI handling. +- It now uses [uv](https://docs.astral.sh/uv/) for dependency management. +- Fixes the issue where extra spaces and newlines were added to the generated + license text. This was considered a bug by at least several users, so it was + fixed in the latest version. However, if you want to generate a license with + the old style, you can use the `--legacy` option or set the `legacy` key in + the configuration file to `true`. +- The code has been modernized and cleaned up, all type-hinting has been + added. +- It passes strict linting with the latest 'Ruff' and 'mypy'. +- GitHub actions set up for linting, `Renovate` and `Dependency Review`. +- Can output a list of licenses and languages in JSON format for integration + with other tools. + +In addition, future plans can be seen in the [TODO.md](TODO.md) file. + +**IMPORTANT** + +> This appllication is now only compatible with Python 3.9 and above. If you +> wish to use an older version, use version **0.6**. + +## Installation + +Installation is standard. If you are using [pipx](https://pipx.pypa.io/) +(recommended) install it as: + +```console +pipx install lice +``` + +Otherwise use `pip` as standard: + +```console +pip install lice +``` + +### Development Version + +If you want to install the development version to try out new features before +they are release, you can do so with the following command: + +```console +pipx install git+https://github.com/licenses/lice.git +``` + +or + +```console +pip install git+https://github.com/licenses/lice.git +``` + +### Autocompletion + +To enable autocompletion for lice options, run the following command after +installation: + +```console +lice --install-completion +``` + +## Overview + +Full usage information is available on the documentation site at + (will look to get the website tranferred over +to the 'lice' address soon) + +Generate a BSD-3 license, the default: + +```console +$ lice +Copyright (c) 2013, Jeremy Carbaugh + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +... +``` + +Generate an MIT license: + +```console +$ lice mit +The MIT License (MIT) +Copyright (c) 2013 Jeremy Carbaugh + +Permission is hereby granted, free of charge, to any person obtaining a copy +... +``` + +Generate a BSD-3 license, specifying the year and organization to be used: + +```console +$ lice -y 2012 -o "Sunlight Foundation" +Copyright (c) 2012, Sunlight Foundation + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +... +``` + +Generate a BSD-3 license, formatted for python source file: + +```console +$ lice -l py + +# Copyright (c) 2012, Sunlight Foundation +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +... +``` + +Generate a python source file with a BSD-3 license commented in the header: + +```console +$ lice -l py -f test +$ ls +test.py +$ cat test.py + +# Copyright (c) 2012, Sunlight Foundation +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +... +``` + +Generate a source file (language detected by -f extension): + +```console +$ lice -f test.c && cat test.c +/* + * Copyright (c) 2012, Sunlight Foundation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + ... +``` + +If organization is not specified, lice will first attempt to use `git config` to +find your name. If not found, it will use the value of the $USER environment +variable. If the project name is not specified, the name of the current +directory is used. Year will default to the current year. + +You can see what variables are available to you for any of the licenses: + +```console +$ lice --vars mit +The mit license template contains the following variables: + year + organization +``` + +## I want XXXXXXXXX license in here! + +Great! Is it a license that is commonly used? If so, open an issue or, if you +are feeling generous, fork and submit a pull request. + +## Usage + +You can get help on the command line with `lice --help`: + +```console +$ lice --help + + Usage: lice [OPTIONS] [license] + + Generates a license template with context variables, and can optionally write this to a file. + +╭─ Arguments ───────────────────────────────────────────────────────────────────────────────────────╮ +│ license_name [license] The license to generate, one of: afl3, agpl3, apache, bsd2, bsd3, │ +│ cc0, cc_by, cc_by_nc, cc_by_nc_nd, cc_by_nc_sa, cc_by_nd, │ +│ cc_by_sa, cddl, epl, gpl2, gpl3, isc, lgpl, mit, mpl, wtfpl, zlib │ +│ [default: bsd3] │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────╮ +│ --header Generate source file header for specified license │ +│ --org -o TEXT Organization, defaults to .gitconfig or os.environ["USER"] │ +│ [default: ] │ +│ --proj -p TEXT Name of project, defaults to name of current directory │ +│ [default: ] │ +│ --template -t TEXT Path to license template file [default: None] │ +│ --year -y TEXT Copyright year [default: ] │ +│ --language -l TEXT Format output for language source file, one of: agda, c, cc, │ +│ clj, cpp, css, el, erl, f, f90, h, hpp, hs, html, idr, java, │ +│ js, lisp, lua, m, ml, php, pl, py, ps, rb, scm, sh, txt, rs │ +│ [default: txt] │ +│ --file -f TEXT Name of the output source file (with -l, extension can be │ +│ ommitted) │ +│ [default: stdout] │ +| --clipboard -c Copy the generated license to the clipboard | +│ --vars List template variables for specified license │ +│ --licenses List available license templates and their parameters │ +│ --languages List available source code formatting languages │ +│ --install-completion Install completion for the current shell. │ +│ --show-completion Show completion for the current shell, to copy it or │ +│ customize the installation. │ +│ --help -h Show this message and exit. │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ + +``` + +## Config File + +The app will look for a config file in `~/.config/lice/config.toml`. This file +can be used to set default values for the license and organization. See the +documentation website for more information. + +```toml +[lice] +default_license = "mit" +organization = "Grant Ramsay" +clipboard = false +legacy = false +``` + +The 'default_license' is checked at run-time, and if it is not valid then it +falls back to the BSD-3 license. + +## Integrate into your projects + +Lice now includes an API that can be imported into your Python projects! This +allows you to generate licenses from within your project. Here is an example: + +```python +from lice.api import Lice + +lice = Lice(organization="Awesome Organization", project="Awesome Project") +license_text = lice.get_license("mit") +print(license_text) +``` + +There are a few other methods available in the API, see the documentation for +more information. + +## Integration with other tools + +This tool can output a list of availailable licenses and languages in JSON +format. This can be used to integrate with other non-Python tools. For example, +to get a list of licenses in JSON format: + +```console +lice --metadata +``` + +The output will have 4 keys: `licenses`, `languages`, `organization` and +`project` which another tool can use to populate a list of licenses and +languages in a GUI for example. The first two keys are simple lists of strings +that can be parsed. + +For more fine-grained control, you can use the API above (but only in Python) + +## Contribute to the Development + +If you want to help with development of this project or just hack on the code, +you can clone the repository and install the development dependencies with the +following commands: + +```console +uv sync +souce .venv/bin/activate +``` + +We use [uv](https://docs.astral.sh/uv/) to manage the virtual environment and +dependencies. See [Contributing](CONTRIBUTING.md) and the relevant section on +the [website](https://seapagan.github.io/lice2/) for details + +All contributions are welcome, and I will try to respond to issues and PR's as +soon as possible. + +## Changelog + +See the [CHANGELOG.md](CHANGELOG.md) file for details for this fork, and the +[OLD_CHANGELOG.md](OLD_CHANGELOG.md) file for the original project. diff --git a/README.rst b/README.rst deleted file mode 100644 index 9f57857..0000000 --- a/README.rst +++ /dev/null @@ -1,171 +0,0 @@ -==== -lice -==== - - -Lice generates license files. No more hunting down licenses from other projects. - -Installation ------------- - -About what you'd expect:: - - pip install lice - - -Overview --------- - -Generate a BSD-3 license, the default:: - - $ lice - Copyright (c) 2013, Jeremy Carbaugh - - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - ... - -Generate an MIT license:: - - $ lice mit - The MIT License (MIT) - Copyright (c) 2013 Jeremy Carbaugh - - Permission is hereby granted, free of charge, to any person obtaining a copy - ... - -Generate a BSD-3 license, specifying the year and organization to be used:: - - $ lice -y 2012 -o "Sunlight Foundation" - Copyright (c) 2012, Sunlight Foundation - - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - ... - -Generate a BSD-3 license, formatted for python source file:: - - $ lice -l py - - # Copyright (c) 2012, Sunlight Foundation - # - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without modification, - ... - -Generate a python source file with a BSD-3 license commented in the header:: - - $ lice -l py -f test - $ ls - test.py - $ cat test.py - - # Copyright (c) 2012, Sunlight Foundation - # - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without modification, - ... - -Generate a source file (language detected by -f extension):: - - $ lice -f test.c && cat test.c - /* - * Copyright (c) 2012, Sunlight Foundation - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - ... - - -If organization is not specified, lice will first attempt to use `git config` to find your name. If not found, it will use the value of the $USER environment variable. If the project name is not specified, the name of the current directory is used. Year will default to the current year. - -You can see what variables are available to you for any of the licenses:: - - $ lice --vars mit - The mit license template contains the following variables: - year - organization - - -I want XXXXXXXXX license in here! ---------------------------------- - -Great! Is it a license that is commonly used? If so, open an issue or, if you are feeling generous, fork and submit a pull request. - - -Usage ------ -:: - - usage: lice [-h] [-o ORGANIZATION] [-p PROJECT] [-t TEMPLATE_PATH] [-y YEAR] - [--vars] [license] - - positional arguments: - license the license to generate, one of: agpl3, apache, bsd2, - bsd3, cddl, cc0, epl, gpl2, gpl3, lgpl, mit, mpl - - optional arguments: - -h, --help show this help message and exit - -o ORGANIZATION, --org ORGANIZATION - organization, defaults to .gitconfig or - os.environ["USER"] - -p PROJECT, --proj PROJECT - name of project, defaults to name of current directory - -t TEMPLATE_PATH, --template TEMPLATE_PATH - path to license template file - -y YEAR, --year YEAR copyright year - -l LANGUAGE, --language LANGUAGE - format output for language source file, one of: js, f, - css, c, m, java, py, cc, h, html, lua, erl, rb, sh, - f90, hpp, cpp, pl, txt [default is not formatted (txt)] - -f OFILE, --file OFILE Name of the output source file (with -l, extension can be omitted) - --vars list template variables for specified license - - -Changelog ---------- - -**0.6** - -* Add PowerShell support (thanks to `danijeljw `_) -* Add Rust support (thanks to `alex179ohm `_) -* Bugfixes (thanks to `ganziqim `_) -* Added support for Python 3.7 and 3.8, removed support for Python 3.4 - -Tested against Python 2.7, 3.5, 3.6, 3.7, and 3.8. - -**0.5** - -* Add support for SCM alias for lisp-style comments (thanks to `ejmr `_) -* Additional support for WTFPL and GPL2 licenses (thanks to `ejmr `_) -* Support for Python 3.4 and 3.5 (thanks to `ejmr `_) - -**0.4** - -* Use ASCII instead of Unicode for templates (thanks to `tabletcorry `_) -* Add Academic Free License ("AFL") v. 3.0 (thanks to `brianray `_) -* Add ISC (thanks to `masklinn `_) -* Add tox support for testing (thanks to `lukaszb `_) -* Show defaults when listing template variables - -**0.3** - -* Generate source file headers for some liceneses -* Discover available licenses at runtime -* Use getpass module for retrieving username -* Better unicode support for Python 3 (thanks to `astagi `_) -* Add Creative Commons licenese (thanks to `rjnienaber `_) - -**0.2** - -* Add AGPL 3 license -* Add extra templates variables to GPL 2 and 3 - -**0.1** - -* Initial release diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..83cd137 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,33 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| >=0.12.0 | :white_check_mark: | + +Until we reach a 1.0 milestone, we will generally only support the latest +release of the project. If you are having issues with an older version, please +upgrade to the latest version and see if the issue is resolved. If not, please +open an Issue. + +If the latest full release does not help, try the latest alpha or beta version +(if available) to see if that fixes the issue. If not, please open an Issue. + +## Reporting a Vulnerability + +If you find a security vulnerability in this code, [please open an +Issue](https://github.com/licenses/lice/issues) and report it. +It may be already known to us and we may be working on a fix. If not, we will +work with you to understand the issue and fix it. Generally we would annouce any +known vulnerability in the +[Discussions](https://github.com/licenses/lice/discussions) +forum. + +We use **Dependabot** for security updates and dependency management. If a +security vulnerability is found in a dependency, Dependabot will issue a PR to +fix this as soon as an updated version of the dependency is available. We will +merge these PRs as soon as they are available. + +Note that this code is provided as-is, and is not guaranteed to be +suitable for any purpose. Use at your own risk. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..84f7b5e --- /dev/null +++ b/TODO.md @@ -0,0 +1,14 @@ +# Future Ideas for the project + + + + +- add a GUI for the license generation (use Textual for this). This would be + accessed through a CLI option or a config file setting. +- add an option to list licenses that have a header available. Maybe this could + be done with a `--list-headers` option or `--headers --list`. +- add a 'human readable' value to the internal LICENSES list so we can display + the license name in a more human readable format. This would be especially + useful for the `--licenses` option and the API. The issue is that this + variable is dynamically generated from the license files, so having a human + readable value would be difficult to maintain. diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 0000000..786b75d --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1 @@ +--8<-- "CHANGELOG.md" diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..9f9d02b --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,37 @@ +# Configuration + +There is an optional configuration file that can be used to customize some of +the application defaults. This is a TOML file that should be named `config.toml` +and placed in the `lice` subdirectory of the appliciable configuration directory +for your operating system. Generally this is `$HOME/.config/` on all operating +systems. + +So for example, on a Linux or Mac system, the configuration file would be +located at `$HOME/.config/lice/config.toml`. + +The TOML file should look like this: + +```toml +[lice] +default_license = "mit" +organization = "Your Organization" +clipboard = false +legacy = false +``` + +Currently there are four options that can be set: + +- `default_license` - This is the default license that will be used if no + license is specified on the command line. If this option is not set, it will + default to `bsd3`. +- `organization` - This is the organization name that will be used in the + license by default. If this is set, it will not try to get the organization + name from `git config` or the `$USER` environment variable. +- `clipboard` - This is a boolean value that will set the default behavior of the + application to copy the generated license to the clipboard. If this option is + not set, it will default to `false`. See the [--clipboard + option](usage.md#-clipboard-c-option) for more information. +- `legacy` - This is a boolean value that will set the default style license + generation to the old style with extra spaces and newlines. If this option is + not set, it will default to `false`. See the [--legacy + option](usage.md#-legacy-option) for more information. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..ea38c9b --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1 @@ +--8<-- "CONTRIBUTING.md" diff --git a/docs/css/extra.css b/docs/css/extra.css new file mode 100644 index 0000000..f1c845c --- /dev/null +++ b/docs/css/extra.css @@ -0,0 +1,12 @@ +.task-list-item2 { + list-style-type: none; +} + +.task-list-item input { + margin: 0 4px 0.25em -4px; + vertical-align: middle; +} + +.md-nav--primary .md-nav__title { + display: none; +} diff --git a/docs/future_plans.md b/docs/future_plans.md new file mode 100644 index 0000000..7cf674f --- /dev/null +++ b/docs/future_plans.md @@ -0,0 +1 @@ +--8<-- "TODO.md" diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..08af9b7 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,45 @@ +# Lice + +Lice generates license files. No more hunting down licenses from other projects. + +!!! note + This project is forked from the original + [lice](https://github.com/licenses/lice){:target="_blank"} project which + seems to have been abandoned and is not compatible with Python 3.12. + + I have created a new project rather than issue a PR because the changes are + quite large, and no-one is merging PR's on the original project. Otherwise, + the Git history is preserved from the original. + +This version fixes the compatibility issue with Python 3.12, and adds some new +features: + +- It has an API that can be imported into your Python projects to allow you to + generate licenses directly from within your own project. +- Can read from a config file for default values. +- Can optionally copy the license to the clipboard automatically. +- Converted from 'argparse' to 'Typer' for CLI handling. +- It now uses [uv](https://docs.astral.sh/uv/){:target="_blank"} for + dependency management. +- Fixes the issue where extra spaces and newlines were added to the generated + license text. This was considered a bug by at least several users, so it was + fixed in version `0.10.0`. However, if you want to generate a license with the + old style, you can use the `--legacy` option or set the `legacy` key in the + configuration file to `true`. +- The code has been modernized and cleaned up, all type-hinting has been + added. +- It passes strict linting with the latest 'Ruff' and 'mypy'. +- GitHub actions set up for linting, `Renovate` and `Dependency Review`. +- Can output a list of licenses and languages in JSON format for integration + with other tools. + +In addition, future plans can be seen in the [Future Plans](future_plans.md) +page. + +!!! warning "Python Compatibility" + This application is now only compatible with Python 3.9 and above. If you + wish to use an older version, use the original 'lice' package. + + However, It's the **development** dependencies that are causing the + incompatibility, so I'll look at reducing the **Production** version in + future releases while still requiring Python 3.9 or above for development. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..3e84d89 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,38 @@ +# Installation + +Installation is standard. If you are using +[pipx](https://pipx.pypa.io/){:target="_blank"} (recommended) install it as: + +```console +pipx install lice +``` + +Otherwise use `pip` as standard: + +```console +pip install lice +``` + +## Development Version + +If you want to install the development version to try out new features before +they are release, you can do so with the following command: + +```console +pipx install git+https://github.com/licences/lice.git +``` + +or + +```console +pip install git+https://github.com/licences/lice.git +``` + +## Autocompletion + +To enable autocompletion for lice options, run the following command after +installation: + +```console +lice --install-completion +``` diff --git a/docs/integration.md b/docs/integration.md new file mode 100644 index 0000000..9d9794b --- /dev/null +++ b/docs/integration.md @@ -0,0 +1,213 @@ +# Integrating 'Lice' in your own project + +## Overview + +Starting version `0.12.0`, you can use `Lice` as an API in your own project to +generate licenses. Here is an example: + +```python +from lice.api import Lice + +lice = Lice(organization="Awesome Organization", project="Awesome Project") +license_text = lice.get_license("mit") +print(license_text) +``` + +This will generate the MIT license text with the organization and project name +replaced with the values you provided, using the current year as the default. + +## Construct a Lice object + +To use `lice` in your own project, you first need to construct a `Lice` object. + +```python +from lice.api import Lice + +lice = Lice(organization="Awesome Organization", project="Awesome Project") +``` + +This is the minimum required to construct a `Lice` object. The organization and +project name are required to generate a license. If you don't provide them, the +class will raise a `TypeError`. + +You can also pass a year to the constructor if you want to use a different year +for any reason (can be useful for testing). This can be an integer or a string. + +```python +from lice.api import Lice + +lice = Lice( + organization="Awesome Organization", + project="Awesome Project", + year="2022" +) +``` + +## Methods + +There are several other methods available in the API. These are called on the +`Lice` object you created. + +### `get_license` + +This method generates a license text based on the license name you provide. The +license name must be a valid (existing) license name. You can get a list of +valid license names using the `get_licenses` method. + +```python +license_text = lice.get_license("mit") +print(license_text) +``` + +```pre +The MIT License (MIT) +Copyright (c) 2024 Grant Ramsay + +Permission is hereby granted, free of charge, to any person obtaining a copy +... +``` + +If you provide an invalid license name, the method will raise a +`lice.exceptions.LicenseNotFoundError` exception. + +You can pass an optional `language` argument to the method to generate the +license text as a commented block in the specified language. This can be useful +for generating license headers in source code files. You can get a list of valid +languages using the `get_languages` method. Note that the value passed should be +the **file extension** of the language (ie 'py' for Python, 'js' for JavaScript +etc) exactly as you would from the CLI. + +```python +license_text = lice.get_license("mit", language="py") +print(license_text) +``` + +```pre +# The MIT License (MIT) +# Copyright (c) 2024 Grant Ramsay +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +... +``` + +If you provide an invalid language name, the method will raise a +`lice.exceptions.LanguageNotFoundError` exception. + +### `get_header` + +Return the header of the given license. This is a stripped-down version of the +license text that is suitable for use as a header in source code files. + +If the language is specified, the header will be formatted as a commented block +for that language. If not, the header will be returned as a plain text block. + +Note: Not all licenses have headers, if the license does not have a header, this +method will raise a `lice.exceptions.HeaderNotFoundError` exception. + +```python +header_text = lice.get_header("gpl3") +print(header_text) +``` + +```pre +lice +Copyright (C) 2024 Grant Ramsay + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +... +``` + +As with the `get_license` method, you can pass an optional `language` argument +to the method to generate the header as a commented block in the specified +language. + +```python +header_text = lice.get_header("gpl3", language="py") +print(header_text) +``` + +```pre +# lice +# Copyright (C) 2024 Grant Ramsay +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +... +``` + +### `get_licenses` + +This method returns a Python `list` of valid license names that can be used with +the other methods. Uesful for generating a list of licenses to display to the +user. + +!!! info + This method will probably be upgraded in the future to return a 'Human + Readable' item also. + +```python +licenses = lice.get_licenses() +print(licenses) +``` + +```pre +['agpl3', 'apache', 'bsd2', 'bsd3', 'cc0', 'epl', 'gpl2', 'gpl3', ...] +``` + +### `get_languages` + +This method returns a Python `list` of valid language names that can be used with +the other methods. Note that these are the standard **file extensions** for the +languages. + +```python +languages = lice.get_languages() +print(languages) +``` + +```pre +['c', 'cpp', 'css', 'html', 'java', 'js', 'json', 'lua', 'py', ...] +``` + +## Exceptions + +There are several exceptions that can be raised by the API methods. These are +all subclasses of `lice.exceptions.LiceError`, and should be caught and +handled. + +They can be imported from the `lice.exceptions` module. + +```python +from lice.exceptions import LicenseNotFoundError +``` + +You can get the offending attribute value by appending it to the `value` +attribute of the exception object. For example, to get the license name that was +invalid, you would access the `.value.license_name`: + +```python +try: + license_text = lice.get_license("invalid") +except LicenseNotFoundError as exc: + print(f"Invalid license name: {exc.value.license_name}") +``` + +### `LicenseNotFoundError` + +Raised when the license name provided to the `get_license` method is not a valid +license name. + +### `LanguageNotFoundError` + +Raised when the language name provided to the `get_license` method is not a +valid language name. + +### `HeaderNotFoundError` + +Raised when the specified license does not have a header available. + +### `InvalidYearError` + +Raised when the year provided to the `Lice` constructor is not a valid year, ie +it is longer than 4 characters or cannot be converted to an integer. diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..aec968b --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,122 @@ +# Overview + +'Lice' is able to generate license files to stdout (the default) or to a file. + +It can also generate reduced 'headers' for longer licenses and format the output +for specific coding languages, optionally saving the output to a file with the +correct extension. + +## Example Usage + +Generate a BSD-3 license, the default + +```console +$ lice +Copyright (c) 2013, Jeremy Carbaugh + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +... +``` + +Generate an MIT license + +```console +$ lice mit +The MIT License (MIT) +Copyright (c) 2013 Jeremy Carbaugh + +Permission is hereby granted, free of charge, to any person obtaining a copy +... +``` + +Generate a BSD-3 license, specifying the year and organization to be used + +```console +$ lice -y 2012 -o "Sunlight Foundation" +Copyright (c) 2012, Sunlight Foundation + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +... +``` + +Generate a BSD-3 license, formatted for python source file: + +```console +$ lice -l py + +# Copyright (c) 2012, Sunlight Foundation +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +... +``` + +Generate a python source file with a BSD-3 license commented in the header: + +```console +$ lice -l py -f test +$ ls +test.py +$ cat test.py + +# Copyright (c) 2012, Sunlight Foundation +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +... +``` + +Generate a source file (language detected by -f extension): + +```console +$ lice -f test.c && cat test.c +/* + * Copyright (c) 2012, Sunlight Foundation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + ... +``` + +If organization is not specified, lice will first attempt to take it from the +config file (if it exists) then use `git config` to find your name. If not +found, it will use the value of the $USER environment variable. If the project +name is not specified, the name of the current directory is used. Year will +default to the current year. + +You can see what variables are available to you for any of the licenses: + +```console +$ lice --vars mit +The mit license template contains the following variables: + year + organization +``` + +## Integrating into your own project + +You can integrate lice in your own project to generate licenses. Here is an +example: + +```python +from lice.api import Lice + +lice = Lice(organization="Awesome Organization", project="Awesome Project") +license_text = lice.get_license("mit") +print(license_text) +``` + +There are a few more methods available in the API, see the +[Integration](integration.md) page for more information. + +## I want XXXXXXXXX license in here! + +Great! Is it a license that is commonly used? If so, open an issue or, if you +are feeling generous, fork and submit a pull request. diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..9c21361 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,233 @@ +# Usage + +## Basic Usage + +At its simplest, `lice` will generate a license header for you to the standard +output. If you don't specify a license, `lice` will default to the BSD-3 +license. + +```console +$ lice + + Copyright (c) 2024, Grant Ramsay + + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + ... +``` + +It will fill in the current year and your name as the copyright holder. + +## Specifying a License + +You can specify a license as the first option. For example, to generate a MIT +license: + +```console +$ lice mit + +The MIT License (MIT) +Copyright (c) 2024 Grant Ramsay + +Permission is hereby granted, free of charge, to any person obtaining a copy +... +``` + +This can be used with any of the options below. Run `lice --licenses` to see a +list of all available licenses. + +## Command Line Options + +`lice` has a number of command line options to customize the output. For a full +list of options, run `lice --help`. + +### `--header` option + +This will generate a brief license header that can be used in source files. + +```console +lice --header +``` + +Again, you can specify a license: + +```console +lice --header apache +``` + +!!! note + The `--header` option is not available for all licenses. If it is not + available, there will be a message to that effect. + +### `--org` / `-o` option + +This will allow you to specify an organization name to be used in the license, +and can be set in the configuration file under the `organization` key. + +```console +lice -o "Awesome Co." +``` + +### `--proj` / `-p` option + +This will allow you to specify a project name to be used in the license. + +```console +lice -p "My Awesome Project" +``` + +!!! note + Not all licenses support the `--proj` option. Run `lice --licences` to see + which licenses support this option. + +### `--template` / `-t` option + +This will allow you to specify a custom template to be used as the license. + +```console +lice -t "./path/to/template.txt" +``` + +### `--year` / `-y` option + +This will allow you to specify a year to be used in the license. If you don't +specify a year, it will default to the current year. + +```console +lice -y 2024 +``` + +### `--language` / `-l` option + +This will allow you to specify a **programming** language to be used in the +license. Specify the **extension** of the file you are creating the license for. + +```console + +lice -l py +``` + +Currently supported languages are: + +agda, c, cc, clj, cpp, css, el, erl, f, f90, h, hpp, hs, html, idr, java, js, +lisp, lua, m, ml, php, pl, py, ps, rb, scm, sh, txt, rs + +### `--file` / `-f` option + +This will allow you to specify a file name to be used in the license, and so the +license will be written to that file instead of the standard output. + +```console +lice mit -f "LICENSE.txt" +``` + +!!! note + If you specify a language with the `-l` option, the extension will be + automatically added to the file name so you don't need to include it. + +### `--clipboard` / `-c` option + +This will automatically copy the generated license to the clipboard. + +```console +lice mit -c +``` + +In this case the license will not be written to the standard output. + +If you are writing to a file with the `-f` option, the clipboard option will +be ignored. This is only implemented for the normal license output to the +terminal and the `--header` option. + +!!! warning + This option may initially fail on some Linux systems, as it requires the + `xclip` or `xsel` command to be installed. You can install one of these with + your package manager. It should work out of the box on macOS or Windows. The + program will give you an informative error message if it fails on how to + install the required program. + +### `--legacy` option + +In the original `lice`, the licenses were generated with a leading space on each +line and extra newlines at start and end. This was considered a bug by at least +several users, so it was fixed in version `0.10.0`. However, if you want to +generate a license with the old style, you can use the `--legacy` option. + +```console +lice mit --legacy +``` + +If you want to use the old style by default, you can set the `legacy` key in the +configuration file to `true`. + +```toml +[lice] +legacy = true +``` + +### `--vars` option + +This will list the variables that can be used in the specified license. + +```console +lice --vars mit +``` + +### `--licenses` option + +This will list all the available licenses and their parameters. + +```console +lice --licenses +``` + +### `--languages` option + +This will list all the available source code formatting languages. + +```console +lice --languages +``` + +### `--metadata` option + +This will output a JSON object containing a list of all the licenses and +languages available. + +```console +lice --metadata +``` + +The output will have 4 keys: `licenses`, `languages`, `organization` and +`project` which another tool can use to populate a list of licenses and +languages in a GUI for example. The first two keys are simple lists of strings +that can be parsed. + +Future versions will have an actual python api that can be imported in other +python projects to generate licenses from within the project. + +### `--install-completion` option + +This will install tab-completion for the current shell. + +```console +lice --install-completion +``` + +### `--show-completion` option + +This will show the tab-completion for the current shell, so you can copy it or +customize the installation. + +```console +lice --show-completion +``` + +### `--help` / `-h` option + +Displays help for the application and it's options. + +```console +lice --help +``` diff --git a/lice/__init__.py b/lice/__init__.py index 297b6f2..44f4748 100644 --- a/lice/__init__.py +++ b/lice/__init__.py @@ -1,6 +1,7 @@ -__version__ = "0.6" +"""Package initialisation.""" +from pathlib import Path -def main(): - from lice.core import main - main() +from single_source import get_version + +__version__ = get_version(__name__, Path(__file__).parent.parent) diff --git a/lice/api/__init__.py b/lice/api/__init__.py new file mode 100644 index 0000000..eccfde1 --- /dev/null +++ b/lice/api/__init__.py @@ -0,0 +1,5 @@ +"""Module to implement the public API of the package.""" + +from .api import Lice + +__all__ = ["Lice"] diff --git a/lice/api/api.py b/lice/api/api.py new file mode 100644 index 0000000..a6ca691 --- /dev/null +++ b/lice/api/api.py @@ -0,0 +1,146 @@ +"""This defines an API that other Python code can use to interact with Lice.""" + +from __future__ import annotations + +from lice.api.exceptions import ( + HeaderNotFoundError, + InvalidYearError, + LanguageNotFoundError, + LicenseNotFoundError, +) +from lice.constants import LANGS, LICENSES +from lice.helpers import ( + format_license, + generate_license, + get_local_year, + load_package_template, +) + + +class Lice: + """List or Generate a License from many supported licenses.""" + + def __init__( + self, + organization: str, + project: str, + year: str | int = get_local_year(), + ) -> None: + """Initialize the Lice object. + + Args: + organization: The name of the organization that owns the project. + project: The name of the project. + year: The year to use in the license. Defaults to the current year. + (can be a string or an integer) + + Note that not all licenses will use the 'project' field. + + Example: + >>> lice = Lice(organization="Awesome Co.", project="my_project") + """ + self.organization = organization + self.project = project + + try: + # make sure the year can be a valid integer + _ = int(year) + except ValueError: + raise InvalidYearError(year) from None + + self.year = str(year) + if len(self.year) != 4: # noqa: PLR2004 + raise InvalidYearError(year) from None + + def get_licenses(self) -> list[str]: + """Return a list of all licenses in the system. + + This returns a list of strings, where each string is the name of a + license that can then be used to generate or retrieve the text of that + license. + + Example: + >>> lice = Lice(organization="Awesome Co.", project="my_project") + >>> lice.get_licenses() + ['apache', 'bsd2', 'bsd3', 'gpl2', 'gpl3', ...] + """ + return LICENSES + + def get_languages(self) -> list[str]: + """Return a list of all supported languages. + + This returns a list of strings, where each string is the name of a + language EXTENSION that can be used to generate a license in that + language format. + + Example: + >>> lice = Lice(organization="Awesome Co.", project="my_project") + >>> lice.get_languages() + ['py', 'js', 'c', 'cpp', 'java', 'rs', 'rb', 'sh', 'html', ...] + """ + return list(LANGS.keys()) + + def get_license(self, license_name: str, language: str = "") -> str: + """Return the text of the given license. + + Args: + license_name: The name of the license to retrieve. + language: [OPTIONAL] If set, comment the license for that language. + + Examples: + >>> lice = Lice(organization="Awesome Co.", project="my_project") + >>> licence_txt = Lice.get_license("mit") + """ + args = { + "year": self.year, + "organization": self.organization, + "project": self.project, + } + try: + template = load_package_template(license_name) + except FileNotFoundError: + raise LicenseNotFoundError(license_name) from None + + content = generate_license(template, args) + + try: + out = format_license(content, language) + except KeyError: + raise LanguageNotFoundError(language) from None + return out.getvalue() + + def get_header(self, license_name: str, language: str = "") -> str: + """Return the header of the given license suitable for source files. + + If the language is specified, the header will be formatted as a + commented block for that language. If not, the header will be returned + as a plain text block. + + Note: Not all licenses have headers, if the license does not have a + header, this method will raise a HeaderNotFoundError. + + Args: + license_name: The name of the license to retrieve. + language: The language to format the header for. + + Example: + >>> lice = Lice(organization="Awesome Co.", project="my_project") + >>> header_txt = Lice.get_header("mit", "py") + """ + args = { + "year": self.year, + "organization": self.organization, + "project": self.project, + } + try: + template = load_package_template(license_name, header=True) + except FileNotFoundError: + raise HeaderNotFoundError(license_name) from None + + content = generate_license(template, args) + + try: + out = format_license(content, language) + except KeyError: + raise LanguageNotFoundError(language) from None + return out.getvalue() diff --git a/lice/api/exceptions.py b/lice/api/exceptions.py new file mode 100644 index 0000000..d5df2e5 --- /dev/null +++ b/lice/api/exceptions.py @@ -0,0 +1,63 @@ +"""Define custom exceptions for the API.""" + +from __future__ import annotations + + +class LiceError(Exception): + """Base class for all exceptions in the Lice API.""" + + +class LicenseNotFoundError(LiceError): + """Raised when a license is not found in the database.""" + + def __init__(self, license_name: str) -> None: + """Initialize the LicenseNotFoundError exception. + + Args: + license_name: The name of the license that was not found. + """ + self.license_name = license_name + super().__init__(f"License '{self.license_name}' is unknown.") + + +class LanguageNotFoundError(LiceError): + """Raised when a language is not found in the database.""" + + def __init__(self, language_name: str) -> None: + """Initialize the LanguageNotFoundError exception. + + Args: + language_name: The name of the language that was not found. + """ + self.language_name = language_name + super().__init__(f"Language '{self.language_name}' is unknown.") + + +class HeaderNotFoundError(LiceError): + """Raised when a header is not found for the supplied license.""" + + def __init__(self, license_name: str) -> None: + """Initialize the NoHeaderFoundError exception. + + Args: + license_name: The name of the license without a header. + """ + self.license_name = license_name + super().__init__( + f"License '{self.license_name}' does not have any headers." + ) + + +class InvalidYearError(LiceError): + """Raised when an invalid year is supplied.""" + + def __init__(self, year: str | int) -> None: + """Initialize the InvalidYearError exception. + + Args: + year: The year that was not valid. + """ + self.year = year + super().__init__( + f"Year '{self.year}' is not a valid year (must be 4 digits)." + ) diff --git a/lice/config.py b/lice/config.py new file mode 100644 index 0000000..5a80b81 --- /dev/null +++ b/lice/config.py @@ -0,0 +1,56 @@ +"""Setup configuration for lice.""" + +from rich.console import Console +from rich.panel import Panel +from simple_toml_settings import TOMLSettings + +from lice.constants import LICENSES + + +class Settings(TOMLSettings): + """Settings for lice.""" + + default_license: str = "bsd3" + organization: str = "" + legacy: bool = False + clipboard: bool = False + + +def check_default_license() -> str: + """Check the default license is in the list of available licenses. + + Return the default license if it is in the list, otherwise return "bsd3". + This is only used to ensure that the configuration file does not have an + invalid default license hence crashing the application, and will be called + automatically by 'Typer' + """ + if settings.default_license not in LICENSES: + console = Console(width=80) + error_text = ( + f"[red]Invalid default license '[b]{settings.default_license}" + "'[/b] in the configuration file, falling back to '[b]bsd3[/b]', " + "unless specified otherwise on the command line.\n\nCheck that [b]" + f"'{settings.get_settings_folder() / settings.settings_file_name}" + f"[/b]' has a valid value for [b]'default_license'[/b]." + ) + panel = Panel( + error_text, + title="[b]Error[/b]", + title_align="left", + expand=False, + style="red", + ) + + console.print() + console.print(panel) + settings.default_license = "bsd3" + return settings.default_license + + +settings = Settings.get_instance( + "lice", + xdg_config=True, + auto_create=False, + allow_missing_file=True, + schema_version="1", +) diff --git a/lice/constants.py b/lice/constants.py new file mode 100644 index 0000000..3be2149 --- /dev/null +++ b/lice/constants.py @@ -0,0 +1,124 @@ +"""Define constants for the LICE package.""" + +import re +from importlib import resources + +# To extend language formatting sopport with a new language, add an item in +# LANGS dict: +# "language_suffix":"comment_name" +# where "language_suffix" is the suffix of your language and "comment_name" is +# one of the comment types supported and listed in LANG_CMT: +# text : no comment +# c : /* * */ +# unix : # +# lua : --- -- + +# if you want add a new comment type just add an item to LANG_CMT: +# "comment_name":['string', 'string', 'string'] +# where the first string open multiline comment, second string comment every +# license's line and the last string close multiline comment, +# associate your language and source file suffix with your new comment type +# how explained above. +# EXAMPLE: +# LANG_CMT = {"c":['/*', '*', '*/']} # noqa: ERA001 +# LANGS = {"cpp":"c"} # noqa: ERA001 +# (for more examples see LANG_CMT and langs dicts below) + +LANGS = { + "ada": "ada", + "adb": "ada", + "ads": "ada", + "agda": "haskell", + "bash": "unix", + "c": "c", + "cc": "c", + "clj": "lisp", + "cpp": "c", + "cs": "c", + "css": "c", + "dart": "c", + "el": "lisp", + "erl": "erlang", + "f": "fortran", + "f90": "fortran90", + "go": "c", + "h": "c", + "hpp": "c", + "hs": "haskell", + "html": "html", + "idr": "haskell", + "java": "java", + "js": "c", + "kt": "java", + "lisp": "lisp", + "lua": "lua", + "m": "c", + "md": "html", + "ml": "ml", + "php": "c", + "pl": "perl", + "ps": "powershell", + "py": "unix", + "rb": "ruby", + "r": "unix", + "rs": "rust", + "scala": "java", + "scm": "lisp", + "sh": "unix", + "sql": "c", + "swift": "c", + "toml": "unix", + "ts": "c", + "txt": "text", + "v": "c", + "vhdl": "ada", + "xml": "html", + "yaml": "unix", +} + +LANG_CMT = { + "ada": ["", "--", ""], + "c": ["/*", " *", " */"], + "erlang": ["%%", "%", "%%"], + "fortran": ["C", "C", "C"], + "fortran90": ["!*", "!*", "!*"], + "haskell": ["{-", "", "-}"], + "html": [""], + "java": ["/**", " *", " */"], + "lisp": ["", ";;", ""], + "lua": ["--[[", "", "--]]"], + "ml": ["(*", "", "*)"], + "perl": ["=item", "", "=cut"], + "powershell": ["<#", "#", "#>"], + "ruby": ["=begin", "", "=end"], + "rust": ["", "//", ""], + "text": ["", "", ""], + "unix": ["", "#", ""], +} + + +def get_available_licenses() -> list[str]: + """Get a sorted list of available license names from template files. + + Searches for templates in the current package's 'templates' directory + with pattern 'template-{name}.txt'. + + Returns: + List of license names sorted alphabetically + """ + # Get the current package name + package_name = __package__ if __package__ else __name__.split(".")[0] + + template_path = resources.files(package_name).joinpath("templates") + licenses = [] + + for file in template_path.iterdir(): + if file.is_file(): + match = re.match(r"template-([a-z0-9_]+)\.txt", file.name) + if match: + licenses.append(match.groups()[0]) + + return sorted(licenses) + + +LICENSES = get_available_licenses() diff --git a/lice/core.py b/lice/core.py index 296b21c..31ee0fc 100644 --- a/lice/core.py +++ b/lice/core.py @@ -1,345 +1,204 @@ -from pkg_resources import (resource_stream, resource_listdir) -from io import StringIO -import argparse -import datetime -import re -import os -import subprocess -import sys -import getpass - - -LICENSES = [] -for file in sorted(resource_listdir(__name__, '.')): - match = re.match(r'template-([a-z0-9_]+).txt', file) - if match: - LICENSES.append(match.groups()[0]) - -DEFAULT_LICENSE = "bsd3" - - -# To extend language formatting sopport with a new language, add an item in -# LANGS dict: -# "language_suffix":"comment_name" -# where "language_suffix" is the suffix of your language and "comment_name" is -# one of the comment types supported and listed in LANG_CMT: -# text : no comment -# c : /* * */ -# unix : # -# lua : --- -- - -# if you want add a new comment type just add an item to LANG_CMT: -# "comment_name":[u'string', u'string', u'string'] -# where the first string open multiline comment, second string comment every -# license's line and the last string close multiline comment, -# associate your language and source file suffix with your new comment type -# how explained above. -# EXAMPLE: -# LANG_CMT = {"c":[u'/*', u'*', u'*/']} -# LANGS = {"cpp":"c"} -# (for more examples see LANG_CMT and langs dicts below) -# NOTE: unicode (u) in comment strings is required. - - -LANGS = { - "agda": "haskell", - "c": "c", - "cc": "c", - "clj": "lisp", - "cpp": "c", - "css": "c", - "el": "lisp", - "erl": "erlang", - "f": "fortran", - "f90": "fortran90", - "h": "c", - "hpp": "c", - "hs": "haskell", - "html": "html", - "idr": "haskell", - "java": "java", - "js": "c", - "lisp": "lisp", - "lua": "lua", - "m": "c", - "ml": "ml", - "php": "c", - "pl": "perl", - "py": "unix", - "ps": "powershell", - "rb": "ruby", - "scm": "lisp", - "sh": "unix", - "txt": "text", - "rs": "rust", -} +"""Main core of the application.""" -LANG_CMT = { - "c": [u'/*', u' *', u' */'], - "erlang": [u'%%', u'%', u'%%'], - "fortran": [u'C', u'C', u'C'], - "fortran90": [u'!*', u'!*', u'!*'], - "haskell": [u'{-', u'', u'-}'], - "html": [u''], - "java": [u'/**', u' *', u' */'], - "lisp": [u'', u';;', u''], - "lua": [u'--[[', u'', u'--]]'], - "ml": [u'(*', u'', u'*)'], - "perl": [u'=item', u'', u'=cut'], - "powershell": [u'<#', u'#', u'#>'], - "ruby": [u'=begin', u'', u'=end'], - "text": [u'', u'', u''], - "unix": [u'', u'#', u''], - "rust": [u'', u'//' u''], -} +from __future__ import annotations - -def clean_path(p): - """ Clean a path by expanding user and environment variables and - ensuring absolute path. +import sys +from pathlib import Path +from types import SimpleNamespace +from typing import Any, Callable, Optional + +import typer +from rich import print as rprint +from rich.markup import escape + +from lice import __version__ +from lice.config import check_default_license, settings +from lice.constants import LANGS, LICENSES +from lice.helpers import ( + copy_to_clipboard, + format_license, + generate_header, + generate_license, + get_context, + get_lang, + get_local_year, + get_metadata, + get_suffix, + guess_organization, + list_languages, + list_licenses, + list_vars, + load_file_template, + load_package_template, + validate_license, + validate_year, +) + +app = typer.Typer(rich_markup_mode="rich") + + +@app.command( + help=( + "Generates a license template with context variables, and " + "optionally write this to a file." + ), + context_settings={"help_option_names": ["-h", "--help"]}, +) +def main( # noqa: PLR0913 + license_name: str = typer.Argument( + default=check_default_license(), + help=f"The license to generate, one of: {', '.join(LICENSES)}", + callback=validate_license, + metavar="[license]", + ), + organization: str = typer.Option( + guess_organization(), + "--org", + "-o", + help='Organization, defaults to .gitconfig or os.environ["USER"]', + ), + project: str = typer.Option( + Path.cwd().name, + "--proj", + "-p", + help="Name of project, defaults to name of current directory", + ), + template_path: Optional[str] = typer.Option( + None, + "--template", + "-t", + help="Path to license template file", + ), + year: Optional[str] = typer.Option( + get_local_year(), + "--year", + "-y", + help="Copyright year", + callback=validate_year, + ), + language: Optional[str] = typer.Option( + None, + "--language", + "-l", + help=( + "Format output for language source file, one of: " + f"{', '.join(LANGS.keys())} " + f"[dim]{escape('[default: txt]')}[/dim]" + ), + show_default=False, + ), + ofile: Optional[str] = typer.Option( + "stdout", + "--file", + "-f", + help=( + "Name of the output source file (with -l, extension can be omitted)" + ), + ), + *, + header: bool = typer.Option( + False, + "--header", + help="Generate source file header for specified license", + ), + clipboard: bool = typer.Option( + False, + "--clipboard", + "-c", + help="Copy the generated license to the clipboard", + ), + show_vars: Optional[bool] = typer.Option( + None, + "--vars", + help="List template variables for specified license", + ), + show_licenses: bool = typer.Option( + False, + "--licenses", + help="List available license templates and their parameters", + ), + show_languages: bool = typer.Option( + False, + "--languages", + help="List available source code formatting languages", + ), + legacy: bool = typer.Option( + False, + "--legacy", + help="Use legacy method to generate license", + ), + version: bool = typer.Option( + False, + "--version", + "-v", + is_eager=True, + help="Show version info", + ), + metadata: bool = typer.Option( + False, + "--metadata", + help=( + "Output a JSON string listing all available licenses and " + "languages This allows easy integration into other tools." + ), + ), +) -> None: + """Generate a license file. + + Can generate a license file, a source file header, or list available + licenses, template variables, and source code formatting. """ - p = os.path.expanduser(p) - p = os.path.expandvars(p) - p = os.path.abspath(p) - return p - - -def get_context(args): - return { - "year": args.year, - "organization": args.organization, - "project": args.project, + # deal with the '--version' flag first + if version: + rprint( + "\n[green]Lice - Generate license files for your projects." + f"\n[/green]Version: {__version__} " + "\u00a9 2013-2024\n" + ) + raise typer.Exit(0) + + # get the args into a dict to avoid refactoring all the code... + args_base: dict[str, str | bool | None] = { + "license": license_name, + "header": header, + "organization": organization, + "project": project, + "template_path": template_path, + "year": year, + "language": language, + "ofile": ofile, + "clipboard": clipboard or settings.clipboard, + "legacy": legacy or settings.legacy, + "list_vars": show_vars, + "list_licenses": show_licenses, + "list_languages": show_languages, } - - -def guess_organization(): - """ Guess the organization from `git config`. If that can't be found, - fall back to $USER environment variable. - """ - try: - stdout = subprocess.check_output('git config --get user.name'.split()) - org = stdout.strip().decode("UTF-8") - except: - org = getpass.getuser() - if sys.version_info[0] == 2: - # only decode when python version is 2.x - org = org.decode("UTF-8") - return org - - -def load_file_template(path): - """ Load template from the specified filesystem path. - """ - template = StringIO() - if not os.path.exists(path): - raise ValueError("path does not exist: %s" % path) - with open(clean_path(path), "rb") as infile: # opened as binary - for line in infile: - template.write(line.decode("utf-8")) # ensure utf-8 - return template - - -def load_package_template(license, header=False): - """ Load license template distributed with package. - """ - content = StringIO() - filename = 'template-%s-header.txt' if header else 'template-%s.txt' - with resource_stream(__name__, filename % license) as licfile: - for line in licfile: - content.write(line.decode("utf-8")) # write utf-8 string - return content - - -def extract_vars(template): - """ Extract variables from template. Variables are enclosed in - double curly braces. - """ - keys = set() - for match in re.finditer(r"\{\{ (?P\w+) \}\}", template.getvalue()): - keys.add(match.groups()[0]) - return sorted(list(keys)) - - -def generate_license(template, context): - """ Generate a license by extracting variables from the template and - replacing them with the corresponding values in the given context. - """ - out = StringIO() - content = template.getvalue() - for key in extract_vars(template): - if key not in context: - raise ValueError("%s is missing from the template context" % key) - content = content.replace("{{ %s }}" % key, context[key]) - template.close() # free template memory (when is garbage collected?) - out.write(content) - return out - - -def format_license(template, lang): - """ Format the StringIO template object for specified lang string: - return StringIO object formatted - """ - if not lang: - lang = 'txt' - out = StringIO() - template.seek(0) # from the start of the buffer - out.write(LANG_CMT[LANGS[lang]][0] + u'\n') - for line in template.readlines(): - out.write(LANG_CMT[LANGS[lang]][1] + u' ') - out.write(line) - out.write(LANG_CMT[LANGS[lang]][2] + u'\n') - template.close() # force garbage collector - return out - - -def get_suffix(name): - """Check if file name have valid suffix for formatting. - if have suffix return it else return False. - """ - a = name.count(".") - if a: - ext = name.split(".")[-1] - if ext in LANGS.keys(): - return ext - return False - else: - return False - - -def main(): - - def valid_year(string): - if not re.match(r"^\d{4}$", string): - raise argparse.ArgumentTypeError("Must be a four digit year") - return string - - parser = argparse.ArgumentParser(description='Generate a license') - - parser.add_argument( - 'license', metavar='license', nargs="?", choices=LICENSES, - help='the license to generate, one of: %s' % ", ".join(LICENSES)) - parser.add_argument( - '--header', dest='header', action="store_true", - help='generate source file header for specified license') - parser.add_argument( - '-o', '--org', dest='organization', default=guess_organization(), - help='organization, defaults to .gitconfig or os.environ["USER"]') - parser.add_argument( - '-p', '--proj', dest='project', default=os.getcwd().split(os.sep)[-1], - help='name of project, defaults to name of current directory') - parser.add_argument( - '-t', '--template', dest='template_path', - help='path to license template file') - parser.add_argument( - '-y', '--year', dest='year', type=valid_year, - default="%i" % datetime.date.today().year, help='copyright year') - parser.add_argument( - '-l', '--language', dest='language', - help='format output for language source file, one of: %s [default is ' - 'not formatted (txt)]' % ', '.join(LANGS.keys())) - parser.add_argument( - '-f', '--file', dest='ofile', default='stdout', - help='Name of the output source file (with -l, ' - 'extension can be ommitted)') - parser.add_argument( - '--vars', dest='list_vars', action="store_true", - help='list template variables for specified license') - parser.add_argument( - '--licenses', dest='list_licenses', action="store_true", - help='list available license templates and their parameters') - parser.add_argument( - '--languages', dest='list_languages', action="store_true", - help='list available source code formatting languages') - - args = parser.parse_args() - - # do license stuff - - license = args.license or DEFAULT_LICENSE - - # language - - lang = args.language - if lang and lang not in LANGS.keys(): - sys.stderr.write("I do not know about a language ending with " - "extension %s.\n" - "Please send a pull request adding this language to\n" - "https://github.com/licenses/lice. Thanks!\n" % lang) - sys.exit(1) - - # generate header if requested - - if args.header: - - if args.template_path: - template = load_file_template(args.template_path) - else: - try: - template = load_package_template(license, header=True) - except IOError: - sys.stderr.write( - "Sorry, no source headers are available for %s.\n" % - args.license) - sys.exit(1) - - content = generate_license(template, get_context(args)) - out = format_license(content, lang) - out.seek(0) - sys.stdout.write(out.getvalue()) - out.close() # free content memory (paranoic memory stuff) - sys.exit(0) - - # list template vars if requested - - if args.list_vars: - - context = get_context(args) - - if args.template_path: - template = load_file_template(args.template_path) - else: - template = load_package_template(license) - - var_list = extract_vars(template) - - if var_list: - sys.stdout.write( - "The %s license template contains the following variables " - "and defaults:\n" % (args.template_path or license)) - for v in var_list: - if v in context: - sys.stdout.write(" %s = %s\n" % (v, context[v])) - else: - sys.stdout.write(" %s\n" % v) - else: - sys.stdout.write( - "The %s license template contains no variables.\n" % - (args.template_path or license)) - - sys.exit(0) - - # list available licenses and their template variables - - if args.list_licenses: - for license in LICENSES: - template = load_package_template(license) - var_list = extract_vars(template) - sys.stdout.write("%s : %s\n" % (license, ", ".join(var_list))) - sys.exit(0) - - # list available source formatting languages - - if args.list_languages: - for lang in sorted(LANGS.keys()): - sys.stdout.write("%s\n" % lang) - sys.exit(0) + # convert to SimpleNamespace, so we can use dot notation + args = SimpleNamespace(**args_base) + + # get the language if set + lang = get_lang(args) + + actions: list[tuple[bool, Callable[..., None], list[Any]]] = [ + (metadata, get_metadata, [args]), + (args.list_licenses, list_licenses, []), + (args.list_languages, list_languages, []), + (header, generate_header, [args, lang]), + (args.list_vars, list_vars, [args, license_name]), + ] + + # Iterate through the list and call the utility functions based on the + # conditions. All the utility functions exit the program after execution. + # This saves us from having to write a lot of if-else statements. + for condition, func, func_args in actions: + if condition: + func(*func_args) # create context - if args.template_path: template = load_file_template(args.template_path) else: - template = load_package_template(license) + template = load_package_template(license_name) content = generate_license(template, get_context(args)) @@ -347,25 +206,26 @@ def valid_year(string): ext = get_suffix(args.ofile) if ext: output = args.ofile - out = format_license(content, ext) # format licese by file suffix + out = format_license( + content, ext, legacy=args.legacy + ) # format license by file suffix else: - if lang: - output = "%s.%s" % (args.ofile, lang) - else: - output = args.ofile - out = format_license(content, lang) + output = f"{args.ofile}.{lang}" if lang else args.ofile + out = format_license(content, lang, legacy=args.legacy) out.seek(0) - with open(output, "w") as f: + with Path(output).open(mode="w") as f: f.write(out.getvalue()) - f.close() else: - out = format_license(content, lang) + out = format_license(content, lang, legacy=args.legacy) out.seek(0) - sys.stdout.write(out.getvalue()) - out.close() # free content memory (paranoic memory stuff) + if not args.clipboard: + sys.stdout.write(out.getvalue()) + else: + copy_to_clipboard(out) -if __name__ == "__main__": - main() + out.close() -# vim: set ts=4 sw=4 tw=79 : + +if __name__ == "__main__": + app() # pragma: no cover diff --git a/lice/helpers.py b/lice/helpers.py new file mode 100644 index 0000000..7f1bb7e --- /dev/null +++ b/lice/helpers.py @@ -0,0 +1,377 @@ +"""Helper functions for lice.""" + +import getpass +import json +import os +import re +import subprocess +import sys +from contextlib import closing +from datetime import datetime +from importlib import resources +from io import StringIO +from pathlib import Path +from types import SimpleNamespace +from typing import Union + +import typer +from rich.console import Console +from rich.table import Table +from rich.text import Text + +from lice.config import settings +from lice.constants import LANG_CMT, LANGS, LICENSES + + +def clean_path(p: str) -> str: + """Clean a path. + + Expand user and environment variables anensuring absolute path. + """ + expanded = os.path.expandvars(Path(p).expanduser()) + return str(Path(expanded).resolve()) + + +def guess_organization() -> str: + """First, try to get fom the settings file. + + If this is blank, guess the organization from `git config`. + If that can't be found, fall back to $USER environment variable. + """ + if settings.organization: + return settings.organization + + try: + stdout = subprocess.check_output("git config --get user.name".split()) # noqa: S603 + org = stdout.strip().decode("UTF-8") + except subprocess.CalledProcessError: + org = getpass.getuser() + return org + + +def get_context(args: SimpleNamespace) -> dict[str, str]: + """Return the context vars from the provided args.""" + return { + "year": args.year, + "organization": args.organization, + "project": args.project, + } + + +def get_lang(args: SimpleNamespace) -> str: + """Check the specified language is supported.""" + lang: str = args.language + if lang and lang not in LANGS: + sys.stderr.write( + "I do not know about a language ending with " + f"extension '{lang}'.\n" + "Please send a pull request adding this language to\n" + "https://github.com/licenses/lice. Thanks!\n" + ) + raise typer.Exit(1) + return lang + + +def list_licenses() -> None: + """List available licenses and their template variables.""" + table = Table(title="Available Licenses") + table.add_column("License Name") + table.add_column("Variables") + for license_name in LICENSES: + template = load_package_template(license_name) + var_list = extract_vars(template) + table.add_row(license_name, ", ".join(var_list)) + + console = Console() + console.print(table) + + raise typer.Exit(0) + + +def list_languages() -> None: + """List available source code formatting languages.""" + console = Console(width=80) + languages = sorted(LANGS.keys()) + text = Text(", ".join(languages)) + console.print( + "The following source code formatting languages are supported:\n" + ) + console.print(text) + + raise typer.Exit(0) + + +def load_file_template(path: str) -> StringIO: + """Load template from the specified filesystem path.""" + template = StringIO() + if not Path(path).exists(): + message = f"path does not exist: {path}" + raise ValueError(message) + with Path(clean_path(path)).open(mode="rb") as infile: # opened as binary + for line in infile: + template.write(line.decode("utf-8")) # ensure utf-8 + return template + + +def get_template_content(license_name: str, *, header: bool = False) -> str: + """Get the content of a license template as a string. + + Args: + license_name: Name of the license template to load + header: If True, load the header template instead of the full license + + Returns: + The template content as a string + + Raises: + FileNotFoundError: If the template doesn't exist + """ + filename = ( + f"template-{license_name}-header.txt" + if header + else f"template-{license_name}.txt" + ) + package_name = __package__ or __name__.split(".")[0] + template_file = resources.files(package_name) / "templates" / filename + return template_file.read_text(encoding="utf-8") + + +def load_package_template( + license_name: str, *, header: bool = False +) -> StringIO: + """Load license template distributed with package. + + Args: + license_name: Name of the license template to load + header: If True, load the header template instead of the full license + + Returns: + StringIO object containing the template content + + Raises: + FileNotFoundError: If the template doesn't exist + """ + content = StringIO() + content.write(get_template_content(license_name, header=header)) + return content + + +def extract_vars(template: StringIO) -> list[str]: + """Extract variables from template. + + Variables are enclosed in double curly braces. + """ + keys: set[str] = set() + for match in re.finditer(r"\{\{ (?P\w+) \}\}", template.getvalue()): + keys.add(match.groups()[0]) + return sorted(keys) + + +def generate_license(template: StringIO, context: dict[str, str]) -> StringIO: + """Generate a license. + + We extract variables from the template and replace them with the + corresponding values in the given context. + + This could be done with a template engine like 'Jinja2, but we're keeping it + simple. + """ + out = StringIO() + with closing(template): + content = template.getvalue() + for key in extract_vars(template): + if key not in context: + message = f"{key} is missing from the template context" + raise ValueError(message) + content = content.replace(f"{{{{ {key} }}}}", context[key]) + out.write(content) + return out + + +def get_comments(lang: str, *, legacy: bool) -> tuple[str, str, str]: + """Adjust the comment strings for the given language. + + The way it was done previously, extra whitespace was added to the start of + the comment lines if the comment was a block comment. This tries to fix + that. + """ + prefix, comment, postfix = LANG_CMT[LANGS[lang]] + if legacy: + return ( + f"{prefix}\n", + f"{comment} ", + f"{postfix}\n", + ) + + if comment: + comment = f"{comment} " + prefix = f"{prefix}\n" if prefix else "" + postfix = f"{postfix}\n" if postfix else "" + return prefix, comment, postfix + + +def format_license( + template: StringIO, lang: str, *, legacy: bool = False +) -> StringIO: + """Format the StringIO template object for specified lang string. + + Return StringIO object formatted + """ + if not lang: + lang = "txt" + + prefix, comment, postfix = get_comments(lang, legacy=legacy) + + out = StringIO() + + with closing(template): + template.seek(0) # from the start of the buffer + out.write(prefix) + for line in template: + # ensure no extra whitespace is added for blank lines + out.write(comment if line.strip() else comment.rstrip()) + out.write(line) + out.write(postfix) + + return out + + +def get_suffix(name: str) -> Union[str, None]: + """Check if file name have valid suffix for formatting. + + If have suffix, return it else return None. + """ + a = name.count(".") + if a: + ext = name.split(".")[-1] + if ext in LANGS: + return ext + return None + + +def list_vars(args: SimpleNamespace, license_name: str) -> None: + """List the variables for the given template.""" + context = get_context(args) + + if args.template_path: + template = load_file_template(args.template_path) + else: + template = load_package_template(license_name) + + var_list = extract_vars(template) + + if var_list: + sys.stdout.write( + "The %s license template contains the following variables " + "and defaults:\n" % (args.template_path or license_name) + ) + for v in var_list: + if v in context: + sys.stdout.write(f" {v} = {context[v]}\n") + else: + sys.stdout.write(f" {v}\n") + else: + sys.stdout.write( + f"The {args.template_path or license_name} license template " + "contains no variables.\n" + ) + + raise typer.Exit(0) + + +def generate_header(args: SimpleNamespace, lang: str) -> None: + """Generate a file header for the given license and language.""" + if args.template_path: + template = load_file_template(args.template_path) + else: + try: + template = load_package_template(args.license, header=True) + except OSError: + sys.stderr.write( + f"Sorry, no source headers are available for {args.license}.\n" + ) + raise typer.Exit(1) from None + + with closing(template): + content = generate_license(template, get_context(args)) + out = format_license(content, lang, legacy=args.legacy) + out.seek(0) + if not args.clipboard: + sys.stdout.write(out.getvalue()) + else: + try: + import pyperclip + + pyperclip.copy(out.getvalue()) + typer.secho( + "License text copied to clipboard", + fg=typer.colors.BRIGHT_GREEN, + ) + except pyperclip.PyperclipException as exc: + typer.secho( + f"Error copying to clipboard: {exc}", + fg=typer.colors.BRIGHT_RED, + ) + raise typer.Exit(2) from None + raise typer.Exit(0) + + +def validate_year(string: str) -> str: + """Validate the year is a four-digit number.""" + if not re.match(r"^\d{4}$", string): + message = "Must be a four-digit year" + raise typer.BadParameter(message) + return string + + +def validate_license(license_name: str) -> str: + """Validate the license is in the list of available licenses.""" + if license_name not in LICENSES: + message = ( + f"License '{license_name}' not found - please run 'lice " + "--licenses' to get a list of available licenses." + ) + raise typer.BadParameter(message) + return license_name + + +def copy_to_clipboard(out: StringIO) -> None: + """Try to copy to clipboard, exit with error if not possible.""" + try: + import pyperclip + + pyperclip.copy(out.getvalue()) + typer.secho( + "License text copied to clipboard", + fg=typer.colors.BRIGHT_GREEN, + ) + except pyperclip.PyperclipException as exc: + typer.secho( + f"Error copying to clipboard: {exc}", + fg=typer.colors.BRIGHT_RED, + ) + raise typer.Exit(2) from None + + +def get_metadata(args: SimpleNamespace) -> None: + """Return metadata for the package as a JSON string.""" + licenses = LICENSES + languages = list(LANGS.keys()) + organization = args.organization + project = args.project + + metadata = { + "languages": languages, + "licenses": licenses, + "organization": organization, + "project": project, + } + + sys.stdout.write(json.dumps(metadata) + "\n") + + raise typer.Exit(0) + + +def get_local_year() -> str: + """Return the current year using the local timezone.""" + return f"{datetime.now().astimezone().year}" diff --git a/lice/templates/__init__.py b/lice/templates/__init__.py new file mode 100644 index 0000000..dcfe706 --- /dev/null +++ b/lice/templates/__init__.py @@ -0,0 +1 @@ +"""This module contains the templates for the lice package.""" diff --git a/lice/template-afl3.txt b/lice/templates/template-afl3.txt similarity index 100% rename from lice/template-afl3.txt rename to lice/templates/template-afl3.txt diff --git a/lice/template-agpl3-header.txt b/lice/templates/template-agpl3-header.txt similarity index 100% rename from lice/template-agpl3-header.txt rename to lice/templates/template-agpl3-header.txt diff --git a/lice/template-agpl3.txt b/lice/templates/template-agpl3.txt similarity index 100% rename from lice/template-agpl3.txt rename to lice/templates/template-agpl3.txt diff --git a/lice/templates/template-al2.txt b/lice/templates/template-al2.txt new file mode 100644 index 0000000..a81bf37 --- /dev/null +++ b/lice/templates/template-al2.txt @@ -0,0 +1,156 @@ +Artistic License 2.0 + +Copyright (c) {{ year }}, {{ organization }} + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. The intent +is that the Copyright Holder maintains some artistic control over the +development of that Package while still keeping the Package available as open +source and free software. + +You are always permitted to make arrangements wholly outside of this license +directly with the Copyright Holder of a given Package. If the terms of this +license do not permit the full use that you propose to make of the Package, you +should contact the Copyright Holder and seek a different licensing arrangement. + +Definitions “Copyright Holder” means the individual(s) or organization(s) named +in the copyright notice for the entire Package. + +“Contributor” means any party that has contributed code or other material to the +Package, in accordance with the Copyright Holder’s procedures. + +“You” and “your” means any person who would like to copy, distribute, or modify +the Package. + +“Package” means the collection of files distributed by the Copyright Holder, and +derivatives of that collection and/or of those files. A given Package may +consist of either the Standard Version, or a Modified Version. + +“Distribute” means providing a copy of the Package or making it accessible to +anyone else, or in the case of a company or organization, to others outside of +your company or organization. + +“Distributor Fee” means any fee that you charge for Distributing this Package or +providing support for this Package to another party. It does not mean licensing +fees. + +“Standard Version” refers to the Package if it has not been modified, or has +been modified only in ways explicitly requested by the Copyright Holder. + +“Modified Version” means the Package, if it has been changed, and such changes +were not explicitly requested by the Copyright Holder. + +“Original License” means this Artistic License as Distributed with the Standard +Version of the Package, in its current version or as it may be modified by The +Perl Foundation in the future. + +“Source” form means the source code, documentation source, and configuration +files for the Package. + +“Compiled” form means the compiled bytecode, object code, binary, or any other +form resulting from mechanical transformation or translation of the Source form. + +Permission for Use and Modification Without Distribution (1) You are permitted +to use the Standard Version and create and use Modified Versions for any purpose +without restriction, provided that you do not Distribute the Modified Version. + +Permissions for Redistribution of the Standard Version (2) You may Distribute +verbatim copies of the Source form of the Standard Version of this Package in +any medium without restriction, either gratis or for a Distributor Fee, provided +that you duplicate all of the original copyright notices and associated +disclaimers. At your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other modifications +made available from the Copyright Holder. The resulting Package will still be +considered the Standard Version, and as such will be subject to the Original +License. + +Distribution of Modified Versions of the Package as Source (4) You may +Distribute your Modified Version as Source (either gratis or for a Distributor +Fee, and with or without a Compiled form of the Modified Version) provided that +you clearly document how it differs from the Standard Version, including, but +not limited to, documenting any non-standard features, executables, or modules, +and provided that you do at least ONE of the following: + +(a) make the Modified Version available to the Copyright Holder of the Standard +Version, under the Original License, so that the Copyright Holder may include +your modifications in the Standard Version. (b) ensure that installation of your +Modified Version does not prevent the user installing or running the Standard +Version. In addition, the Modified Version must bear a name that is different +from the name of the Standard Version. (c) allow anyone who receives a copy of +the Modified Version to make the Source form of the Modified Version available +to others under (i) the Original License or (ii) a license that permits the +licensee to freely copy, modify and redistribute the Modified Version using the +same licensing terms that apply to the copy that the licensee received, and +requires that the Source form of the Modified Version, and of any works derived +from it, be made freely available in that license fees are prohibited but +Distributor Fees are allowed. + +Distribution of Compiled Forms of the Standard Version or Modified Versions +without the Source (5) You may Distribute Compiled forms of the Standard Version +without the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be valid at the +time of your distribution. If these instructions, at any time while you are +carrying out such distribution, become invalid, you must provide new +instructions on demand or cease further distribution. If you provide valid +instructions or cease distribution within thirty days after you become aware +that the instructions are invalid, then you do not forfeit any of your rights +under this license. + +(6) You may Distribute a Modified Version in Compiled form without the Source, +provided that you comply with Section 4 with respect to the Source of the +Modified Version. + +Aggregating or Linking the Package (7) You may aggregate the Package (either the +Standard Version or Modified Version) with other packages and Distribute the +resulting aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other components +in the aggregation are permitted. The terms of this license apply to the use and +Distribution of the Standard or Modified Versions as included in the +aggregation. + +(8) You are permitted to link Modified and Standard Versions with other works, +to embed the Package in a larger work of your own, or to build stand-alone +binary or bytecode versions of applications that include the Package, and +Distribute the result without restriction, provided the result does not expose a +direct interface to the Package. + +Items That are Not Considered Part of a Modified Version (9) Works (including, +but not limited to, modules and scripts) that merely extend or make use of the +Package, do not, by themselves, cause the Package to be a Modified Version. In +addition, such works are not considered parts of the Package itself, and are not +subject to the terms of this license. + +General Provisions (10) Any use, modification, and distribution of the Standard +or Modified Versions is governed by this Artistic License. By using, modifying +or distributing the Package, you accept this license. Do not use, modify, or +distribute the Package, if you do not accept this license. + +(11) If your Modified Version has been derived from a Modified Version made by +someone other than you, you are nevertheless required to ensure that your +Modified Version complies with the requirements of this license. + +(12) This license does not grant you the right to use any trademark, service +mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, free-of-charge patent +license to make, have made, use, offer to sell, sell, import and otherwise +transfer the Package with respect to any patent claims licensable by the +Copyright Holder that are necessarily infringed by the Package. If you institute +patent litigation (including a cross-claim or counterclaim) against any party +alleging that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the date that +such litigation is filed. + +(14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND +CONTRIBUTORS “AS IS’ AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. +UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY +OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/lice/template-apache-header.txt b/lice/templates/template-apache-header.txt similarity index 100% rename from lice/template-apache-header.txt rename to lice/templates/template-apache-header.txt diff --git a/lice/template-apache.txt b/lice/templates/template-apache.txt similarity index 100% rename from lice/template-apache.txt rename to lice/templates/template-apache.txt diff --git a/lice/template-bsd2.txt b/lice/templates/template-bsd2.txt similarity index 100% rename from lice/template-bsd2.txt rename to lice/templates/template-bsd2.txt diff --git a/lice/template-bsd3.txt b/lice/templates/template-bsd3.txt similarity index 100% rename from lice/template-bsd3.txt rename to lice/templates/template-bsd3.txt diff --git a/lice/template-cc0-header.txt b/lice/templates/template-cc0-header.txt similarity index 97% rename from lice/template-cc0-header.txt rename to lice/templates/template-cc0-header.txt index ed335cb..58b294c 100644 --- a/lice/template-cc0-header.txt +++ b/lice/templates/template-cc0-header.txt @@ -5,4 +5,4 @@ To the extent possible under law, the person who associated CC0 with to {{ project }}. You should have received a copy of the CC0 legalcode along with this -work. If not, see . \ No newline at end of file +work. If not, see . diff --git a/lice/template-cc0.txt b/lice/templates/template-cc0.txt similarity index 100% rename from lice/template-cc0.txt rename to lice/templates/template-cc0.txt diff --git a/lice/template-cc_by-header.txt b/lice/templates/template-cc_by-header.txt similarity index 99% rename from lice/template-cc_by-header.txt rename to lice/templates/template-cc_by-header.txt index 98898d2..1756f24 100644 --- a/lice/template-cc_by-header.txt +++ b/lice/templates/template-cc_by-header.txt @@ -4,4 +4,4 @@ Creative Commons Attribution 3.0 Unported License. You should have received a copy of the license along with this -work. If not, see . \ No newline at end of file +work. If not, see . diff --git a/lice/template-cc_by.txt b/lice/templates/template-cc_by.txt similarity index 100% rename from lice/template-cc_by.txt rename to lice/templates/template-cc_by.txt diff --git a/lice/template-cc_by_nc-header.txt b/lice/templates/template-cc_by_nc-header.txt similarity index 98% rename from lice/template-cc_by_nc-header.txt rename to lice/templates/template-cc_by_nc-header.txt index 7f5fecd..d03a52d 100644 --- a/lice/template-cc_by_nc-header.txt +++ b/lice/templates/template-cc_by_nc-header.txt @@ -4,4 +4,4 @@ Creative Commons Attribution-NonCommercial 3.0 Unported License. You should have received a copy of the license along with this -work. If not, see . \ No newline at end of file +work. If not, see . diff --git a/lice/template-cc_by_nc.txt b/lice/templates/template-cc_by_nc.txt similarity index 100% rename from lice/template-cc_by_nc.txt rename to lice/templates/template-cc_by_nc.txt diff --git a/lice/template-cc_by_nc_nd-header.txt b/lice/templates/template-cc_by_nc_nd-header.txt similarity index 97% rename from lice/template-cc_by_nc_nd-header.txt rename to lice/templates/template-cc_by_nc_nd-header.txt index 1e62200..260a05b 100644 --- a/lice/template-cc_by_nc_nd-header.txt +++ b/lice/templates/template-cc_by_nc_nd-header.txt @@ -4,4 +4,4 @@ Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License. You should have received a copy of the license along with this -work. If not, see . \ No newline at end of file +work. If not, see . diff --git a/lice/template-cc_by_nc_nd.txt b/lice/templates/template-cc_by_nc_nd.txt similarity index 100% rename from lice/template-cc_by_nc_nd.txt rename to lice/templates/template-cc_by_nc_nd.txt diff --git a/lice/template-cc_by_nc_sa-header.txt b/lice/templates/template-cc_by_nc_sa-header.txt similarity index 97% rename from lice/template-cc_by_nc_sa-header.txt rename to lice/templates/template-cc_by_nc_sa-header.txt index cf7fb7a..aee575c 100644 --- a/lice/template-cc_by_nc_sa-header.txt +++ b/lice/templates/template-cc_by_nc_sa-header.txt @@ -4,4 +4,4 @@ Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. You should have received a copy of the license along with this -work. If not, see . \ No newline at end of file +work. If not, see . diff --git a/lice/template-cc_by_nc_sa.txt b/lice/templates/template-cc_by_nc_sa.txt similarity index 99% rename from lice/template-cc_by_nc_sa.txt rename to lice/templates/template-cc_by_nc_sa.txt index d435b18..a632f3e 100644 --- a/lice/template-cc_by_nc_sa.txt +++ b/lice/templates/template-cc_by_nc_sa.txt @@ -358,4 +358,3 @@ Creative Commons Notice this trademark restriction does not form part of this License. Creative Commons may be contacted at http://creativecommons.org/. - diff --git a/lice/template-cc_by_nd-header.txt b/lice/templates/template-cc_by_nd-header.txt similarity index 98% rename from lice/template-cc_by_nd-header.txt rename to lice/templates/template-cc_by_nd-header.txt index 1194ef0..20be1af 100644 --- a/lice/template-cc_by_nd-header.txt +++ b/lice/templates/template-cc_by_nd-header.txt @@ -4,4 +4,4 @@ Creative Commons Attribution-NoDerivs 3.0 Unported License. You should have received a copy of the license along with this -work. If not, see . \ No newline at end of file +work. If not, see . diff --git a/lice/template-cc_by_nd.txt b/lice/templates/template-cc_by_nd.txt similarity index 100% rename from lice/template-cc_by_nd.txt rename to lice/templates/template-cc_by_nd.txt diff --git a/lice/template-cc_by_sa-header.txt b/lice/templates/template-cc_by_sa-header.txt similarity index 98% rename from lice/template-cc_by_sa-header.txt rename to lice/templates/template-cc_by_sa-header.txt index 667df41..440819c 100644 --- a/lice/template-cc_by_sa-header.txt +++ b/lice/templates/template-cc_by_sa-header.txt @@ -4,4 +4,4 @@ Creative Commons Attribution-ShareAlike 3.0 Unported License. You should have received a copy of the license along with this -work. If not, see . \ No newline at end of file +work. If not, see . diff --git a/lice/template-cc_by_sa.txt b/lice/templates/template-cc_by_sa.txt similarity index 100% rename from lice/template-cc_by_sa.txt rename to lice/templates/template-cc_by_sa.txt diff --git a/lice/template-cddl.txt b/lice/templates/template-cddl.txt similarity index 100% rename from lice/template-cddl.txt rename to lice/templates/template-cddl.txt diff --git a/lice/templates/template-edl.txt b/lice/templates/template-edl.txt new file mode 100644 index 0000000..05fd3f6 --- /dev/null +++ b/lice/templates/template-edl.txt @@ -0,0 +1,28 @@ +Eclipse Distribution License - v 1.0 + +Copyright (c) {{ year }}, {{ organization }} + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Eclipse Foundation, Inc. nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lice/template-epl.txt b/lice/templates/template-epl.txt similarity index 100% rename from lice/template-epl.txt rename to lice/templates/template-epl.txt diff --git a/lice/templates/template-eupl.txt b/lice/templates/template-eupl.txt new file mode 100644 index 0000000..c4ffb95 --- /dev/null +++ b/lice/templates/template-eupl.txt @@ -0,0 +1,288 @@ + +EUROPEAN UNION PUBLIC LICENCE v. 1.2 +EUPL © {{ organization }} {{ year }} + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined +below) which is provided under the terms of this Licence. Any use of the Work, +other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). + +The Work is provided under the terms of this Licence when the Licensor (as +defined below) has placed the following notice immediately following the +copyright notice for the Work: + +Licensed under the EUPL + +or has expressed by any other means his willingness to license under the EUPL. + +1. Definitions + +In this Licence, the following terms have the following meaning: + +- ‘The Licence’: this Licence. + +- ‘The Original Work’: the work or software distributed or communicated by the +Licensor under this Licence, available as Source Code and also as Executable +Code as the case may be. + +- ‘Derivative Works’: the works or software that could be created by the +Licensee, based upon the Original Work or modifications thereof. This Licence +does not define the extent of modification or dependence on the Original Work +required in order to classify a work as a Derivative Work; this extent is +determined by copyright law applicable in the country mentioned in Article 15. + +- ‘The Work’: the Original Work or its Derivative Works. + +- ‘The Source Code’: the human-readable form of the Work which is the most +convenient for people to study and modify. + +- ‘The Executable Code’: any code which has generally been compiled and which is +meant to be interpreted by a computer as a program. + +- ‘The Licensor’: the natural or legal person that distributes or communicates +the Work under the Licence. + +- ‘Contributor(s)’: any natural or legal person who modifies the Work under the +Licence, or otherwise contributes to the creation of a Derivative Work. + +- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of +the Work under the terms of the Licence. + +- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, +renting, distributing, communicating, transmitting, or otherwise making +available, online or offline, copies of the Work or providing access to its +essential functionalities at the disposal of any other natural or legal +person. + +2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +sublicensable licence to do the following, for the duration of copyright vested +in the Original Work: + +- use the Work in any circumstance and for all usage, +- reproduce the Work, +- modify the Work, and make Derivative Works based upon the Work, +- communicate to the public, including the right to make available or display +the Work or copies thereof to the public and perform publicly, as the case may +be, the Work, +- distribute the Work or copies thereof, +- lend and rent the Work or copies thereof, +- sublicense rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to +any patents held by the Licensor, to the extent necessary to make use of the +rights granted on the Work under this Licence. + +3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, in +a notice following the copyright notice attached to the Work, a repository where +the Source Code is easily and freely accessible for as long as the Licensor +continues to distribute or communicate the Work. + +4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits from +any exception or limitation to the exclusive rights of the rights owners in the +Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and a +copy of the Licence with every copy of the Work he/she distributes or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the +Original Works or Derivative Works, this Distribution or Communication will be +done under the terms of this Licence or of a later version of this Licence +unless the Original Work is expressly distributed only under this version of the +Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee +(becoming Licensor) cannot offer or impose any additional terms or conditions on +the Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative +Works or copies thereof based upon both the Work and another work licensed under +a Compatible Licence, this Distribution or Communication can be done under the +terms of this Compatible Licence. For the sake of this clause, ‘Compatible +Licence’ refers to the licences listed in the appendix attached to this Licence. +Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible +Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, +the Licensee will provide a machine-readable copy of the Source Code or indicate +a repository where this Source will be easily and freely available for as long +as the Licensee continues to distribute or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, +trademarks, service marks, or names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + +7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +Contributors. It is not a finished work and may therefore contain defects or +‘bugs’ inherent to this type of development. + +For the above reason, the Work is provided under the Licence on an ‘as is’ basis +and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other than +copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + +8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the use +of the Work, including without limitation, damages for loss of goodwill, work +stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such damage. +However, the Licensor will be liable under statutory product liability laws as +far such laws apply to the Work. + +9. Additional agreements + +While distributing the Work, You may choose to conclude an additional agreement, +defining obligations or services consistent with this Licence. However, if +accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, +and only if You agree to indemnify, defend, and hold each Contributor harmless +for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ +placed under the bottom of a window displaying the text of this Licence or by +affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this Licence, +such as the use of the Work, the creation by You of a Derivative Work or the +Distribution or Communication by You of the Work or copies thereof. + +11. Information to the public + +In case of any Distribution or Communication of the Work by means of electronic +communication by You (for example, by offering to download the Work from a +remote location) the distribution channel or media (for example, a website) must +at least provide to the public the information requested by the applicable law +regarding the Licensor, the Licence and the way it may be accessible, concluded, +stored and reproduced by the Licensee. + +12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + +13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed or reformed so as necessary to make it +valid and enforceable. + +The European Commission may publish other linguistic versions or new versions of +this Licence or updated versions of the Appendix, so far this is required and +reasonable, without reducing the scope of the rights granted by the Licence. New +versions of the Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + +14. Jurisdiction + +Without prejudice to specific agreement between parties, + +- any litigation resulting from the interpretation of this License, arising +between the European Union institutions, bodies, offices or agencies, as a +Licensor, and any Licensee, will be subject to the jurisdiction of the Court +of Justice of the European Union, as laid down in article 272 of the Treaty on +the Functioning of the European Union, + +- any litigation arising between other parties and resulting from the +interpretation of this License, will be subject to the exclusive jurisdiction +of the competent court where the Licensor resides or conducts its primary +business. + +15. Applicable Law + +Without prejudice to specific agreement between parties, + +- this Licence shall be governed by the law of the European Union Member State +where the Licensor has his seat, resides or has his registered office, + +- this licence shall be governed by Belgian law if the Licensor has no seat, +residence or registered office inside a European Union Member State. + +Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: + +- GNU General Public License (GPL) v. 2, v. 3 +- GNU Affero General Public License (AGPL) v. 3 +- Open Software License (OSL) v. 2.1, v. 3.0 +- Eclipse Public License (EPL) v. 1.0 +- CeCILL v. 2.0, v. 2.1 +- Mozilla Public Licence (MPL) v. 2 +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for +works other than software +- European Union Public Licence (EUPL) v. 1.1, v. 1.2 +- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong +Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above +licences without producing a new version of the EUPL, as long as they provide +the rights granted in Article 2 of this Licence and protect the covered Source +Code from exclusive appropriation. + +All other changes or additions to this Appendix require the production of a new +EUPL version. diff --git a/lice/template-gpl2-header.txt b/lice/templates/template-gpl2-header.txt similarity index 100% rename from lice/template-gpl2-header.txt rename to lice/templates/template-gpl2-header.txt diff --git a/lice/template-gpl2.txt b/lice/templates/template-gpl2.txt similarity index 100% rename from lice/template-gpl2.txt rename to lice/templates/template-gpl2.txt diff --git a/lice/template-gpl3-header.txt b/lice/templates/template-gpl3-header.txt similarity index 100% rename from lice/template-gpl3-header.txt rename to lice/templates/template-gpl3-header.txt diff --git a/lice/template-gpl3.txt b/lice/templates/template-gpl3.txt similarity index 100% rename from lice/template-gpl3.txt rename to lice/templates/template-gpl3.txt diff --git a/lice/template-isc.txt b/lice/templates/template-isc.txt similarity index 100% rename from lice/template-isc.txt rename to lice/templates/template-isc.txt diff --git a/lice/template-lgpl.txt b/lice/templates/template-lgpl.txt similarity index 100% rename from lice/template-lgpl.txt rename to lice/templates/template-lgpl.txt diff --git a/lice/template-mit.txt b/lice/templates/template-mit.txt similarity index 100% rename from lice/template-mit.txt rename to lice/templates/template-mit.txt diff --git a/lice/template-mpl-header.txt b/lice/templates/template-mpl-header.txt similarity index 100% rename from lice/template-mpl-header.txt rename to lice/templates/template-mpl-header.txt diff --git a/lice/template-mpl.txt b/lice/templates/template-mpl.txt similarity index 99% rename from lice/template-mpl.txt rename to lice/templates/template-mpl.txt index 14e2f77..a612ad9 100644 --- a/lice/template-mpl.txt +++ b/lice/templates/template-mpl.txt @@ -35,7 +35,7 @@ Mozilla Public License Version 2.0 means any form of the work other than Source Code Form. 1.7. "Larger Work" - means a work that combines Covered Software with other material, in + means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" diff --git a/lice/templates/template-ofl.txt b/lice/templates/template-ofl.txt new file mode 100644 index 0000000..d1f8579 --- /dev/null +++ b/lice/templates/template-ofl.txt @@ -0,0 +1,96 @@ + +Copyright (c) {{ year }}, {{ organization }}, +with Reserved Font Name . + + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/lice/templates/template-unlicense.txt b/lice/templates/template-unlicense.txt new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/lice/templates/template-unlicense.txt @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/lice/template-wtfpl-header.txt b/lice/templates/template-wtfpl-header.txt similarity index 100% rename from lice/template-wtfpl-header.txt rename to lice/templates/template-wtfpl-header.txt diff --git a/lice/template-wtfpl.txt b/lice/templates/template-wtfpl.txt similarity index 100% rename from lice/template-wtfpl.txt rename to lice/templates/template-wtfpl.txt diff --git a/lice/template-zlib.txt b/lice/templates/template-zlib.txt similarity index 100% rename from lice/template-zlib.txt rename to lice/templates/template-zlib.txt diff --git a/lice/tests/__init__.py b/lice/tests/__init__.py index e69de29..9f42de6 100644 --- a/lice/tests/__init__.py +++ b/lice/tests/__init__.py @@ -0,0 +1 @@ +"""This module contains the test suite.""" diff --git a/lice/tests/conftest.py b/lice/tests/conftest.py new file mode 100644 index 0000000..a2a58c7 --- /dev/null +++ b/lice/tests/conftest.py @@ -0,0 +1,69 @@ +"""Setup the test configuration for the lice tests.""" + +from __future__ import annotations + +from pathlib import Path +from types import SimpleNamespace +from typing import TYPE_CHECKING + +import pytest + +from lice.api import Lice + +if TYPE_CHECKING: + from pyfakefs.fake_filesystem import FakeFilesystem + +TEMPLATE_FILE = """This is a template file. +{{ organization }} is the organization. +{{ project }} is the project. +{{ year }} is the year. +""" + + +@pytest.fixture +def lice() -> Lice: + """Return a Lice instance for testing the 'api' module.""" + return Lice( + organization="Awesome Co.", + project="my_project", + ) + + +@pytest.fixture(autouse=True) +def fake_config(fs: FakeFilesystem) -> FakeFilesystem: + """Fixture to setup the fake filesystem for the tests. + + This stops any tests interacting with the real filesystem. + """ + # 'fs' is the fake filesystem object + fs.create_file( + Path.home() / ".config/lice/lice.toml", + contents="default_license = 'mit'\norganization = 'Awesome Co.'", + ) + + fs.create_file(Path.home() / "template.txt", contents=TEMPLATE_FILE) + + # copy over the license templates so we can use them in the tests + fs.add_real_directory(Path(__file__).parent.parent / "templates") + return fs + + +@pytest.fixture +def args() -> SimpleNamespace: + """Fixture to return a default args object.""" + args_base: dict[str, str | bool | None] = { + "license": "mit", + "header": False, + "organization": "Awesome Co.", + "project": "my_project", + "template_path": None, + "year": "2024", + "language": None, + "ofile": None, + "clipboard": False, + "legacy": False, + "list_vars": False, + "list_licenses": False, + "list_languages": False, + } + return SimpleNamespace(**args_base) diff --git a/lice/tests/test_api.py b/lice/tests/test_api.py new file mode 100644 index 0000000..514a08c --- /dev/null +++ b/lice/tests/test_api.py @@ -0,0 +1,187 @@ +"""Test suite for the programmatic API of lice.""" + +import pytest + +from lice.api import Lice +from lice.api.exceptions import ( + HeaderNotFoundError, + InvalidYearError, + LanguageNotFoundError, + LicenseNotFoundError, +) +from lice.constants import LANGS, LICENSES +from lice.helpers import get_local_year + + +class TestAPI: + """A test class for the API.""" + + def test_lice_instance(self, lice: Lice) -> None: + """Test that the Lice instance is created correctly.""" + assert isinstance(lice, Lice) + assert lice.organization == "Awesome Co." + assert lice.project == "my_project" + + def test_lice_instance_no_args(self) -> None: + """Test that creating a Lice instance with no args fails.""" + with pytest.raises( + TypeError, match="missing 2 required positional arguments" + ): + Lice() # type: ignore[call-arg] + + def test_lice_instance_invalid_year(self) -> None: + """Test that creating a Lice instance with an invalid year fails.""" + with pytest.raises(InvalidYearError) as exc_info: + Lice(organization="Awesome Co.", project="my_project", year="202") + + assert "'202' is not a valid year" in str(exc_info.value) + + def test_lice_instance_very_bad_year(self) -> None: + """Test that creating a Lice instance with a very bad year fails. + + This is basically anything that cannot be converted to an integer. + """ + with pytest.raises(InvalidYearError) as exc_info: + Lice(organization="Awesome Co.", project="my_project", year="bad1") + + assert "'bad1' is not a valid year" in str(exc_info.value) + + def test_lice_instance_integer_year(self) -> None: + """Test that creating a Lice instance with an integer year works.""" + lice = Lice(organization="Awesome Co.", project="my_project", year=1314) + assert lice.year == "1314" + + def test_license_not_found_error_no_arg(self) -> None: + """Test that raising LicenseNotFoundError without an argument fails.""" + with pytest.raises(TypeError): + raise LicenseNotFoundError # type: ignore[call-arg] + + def test_language_not_found_error_no_arg(self) -> None: + """Test that raising LanguageNotFoundError without an argument fails.""" + with pytest.raises(TypeError): + raise LanguageNotFoundError # type: ignore[call-arg] + + def test_header_not_found_error_no_arg(self) -> None: + """Test that raising HeaderNotFoundError without an argument fails.""" + with pytest.raises(TypeError): + raise HeaderNotFoundError # type: ignore[call-arg] + + def test_invalid_year_error_no_arg(self) -> None: + """Test that raising InvalidYearError without an argument fails.""" + with pytest.raises(TypeError): + raise InvalidYearError # type: ignore[call-arg] + + def test_invalid_year_error(self) -> None: + """Test that InvalidYearError is raised correctly.""" + bad_year = "202" + with pytest.raises(InvalidYearError) as exc_info: + raise InvalidYearError(bad_year) + assert "Year '202' is not a valid year" in str(exc_info.value) + assert exc_info.value.year == bad_year + + def test_license_not_found_error(self) -> None: + """Test that LicenseNotFoundError is raised correctly.""" + bad_license = "unknown_license" + with pytest.raises(LicenseNotFoundError) as exc_info: + raise LicenseNotFoundError(bad_license) + assert str(exc_info.value) == "License 'unknown_license' is unknown." + assert exc_info.value.license_name == "unknown_license" + + def test_language_not_found_error(self) -> None: + """Test that LanguageNotFoundError is raised correctly.""" + bad_language = "unknown_language" + with pytest.raises(LanguageNotFoundError) as exc_info: + raise LanguageNotFoundError(bad_language) + assert str(exc_info.value) == "Language 'unknown_language' is unknown." + assert exc_info.value.language_name == "unknown_language" + + def test_header_not_found_error(self) -> None: + """Test that HeaderNotFoundError is raised correctly.""" + license_name = "unknown_license" + with pytest.raises(HeaderNotFoundError) as exc_info: + raise HeaderNotFoundError(license_name) + assert ( + str(exc_info.value) + == "License 'unknown_license' does not have any headers." + ) + assert exc_info.value.license_name == "unknown_license" + + def test_get_licenses(self, lice: Lice) -> None: + """Test that get_licenses returns a list of licenses.""" + licenses = lice.get_licenses() + assert isinstance(licenses, list) + assert len(licenses) == len(LICENSES) + assert all(isinstance(license_name, str) for license_name in licenses) + + def test_get_languages(self, lice: Lice) -> None: + """Test that get_languages returns a list of languages.""" + languages = lice.get_languages() + assert isinstance(languages, list) + assert len(languages) == len(LANGS.keys()) + assert all(isinstance(language, str) for language in languages) + + def test_get_license(self, lice: Lice) -> None: + """Test we can get a standard license. + + We'll use the AFL3 license as it uses all 3 context vars. + """ + license_txt = lice.get_license("afl3") + + this_year = get_local_year() + + assert "Academic Free License" in license_txt + assert "Awesome Co." in license_txt + assert "my_project" in license_txt + assert str(this_year) in license_txt + + def test_get_license_unknown(self, lice: Lice) -> None: + """Test that get_license raises an exception for unknown licenses.""" + with pytest.raises(LicenseNotFoundError) as exc_info: + lice.get_license("unknown_license") + assert str(exc_info.value) == "License 'unknown_license' is unknown." + assert exc_info.value.license_name == "unknown_license" + + def test_get_license_language(self, lice: Lice) -> None: + """Test we can get a license for a specific language.""" + license_txt = lice.get_license("mit", language="py") + + assert "The MIT License (MIT)" in license_txt + for line in license_txt.splitlines(): + assert line.startswith("#") + + def test_get_license_language_unknown(self, lice: Lice) -> None: + """Test that get_license raises an exception for unknown languages.""" + with pytest.raises(LanguageNotFoundError) as exc_info: + lice.get_license("mit", language="unknown_language") + assert str(exc_info.value) == "Language 'unknown_language' is unknown." + assert exc_info.value.language_name == "unknown_language" + + def test_get_license_header(self, lice: Lice) -> None: + """Test we can get a license header.""" + header = lice.get_header("gpl3") + + assert "This program is free software:" in header + assert "GNU General Public License" in header + + def test_get_license_header_language(self, lice: Lice) -> None: + """Test we can get a license header for a specific language.""" + header = lice.get_header("gpl3", language="py") + + assert "This program is free software:" in header + assert "GNU General Public License" in header + for line in header.splitlines(): + assert line.startswith("#") + + def test_get_license_header_not_exists(self, lice: Lice) -> None: + """Test that get_license_header raises an exception for no header.""" + with pytest.raises(HeaderNotFoundError) as exc_info: + lice.get_header("mit") + assert str(exc_info.value) == "License 'mit' does not have any headers." + assert exc_info.value.license_name == "mit" + + def test_get_license_header_language_unknown(self, lice: Lice) -> None: + """Test get_license_header raises an exception for unknown languages.""" + with pytest.raises(LanguageNotFoundError) as exc_info: + lice.get_header("gpl3", language="unknown_language") + assert str(exc_info.value) == "Language 'unknown_language' is unknown." + assert exc_info.value.language_name == "unknown_language" diff --git a/lice/tests/test_cli.py b/lice/tests/test_cli.py new file mode 100644 index 0000000..0593aa3 --- /dev/null +++ b/lice/tests/test_cli.py @@ -0,0 +1,140 @@ +"""Tests for the command line interface.""" + +from io import StringIO + +from pyperclip import PyperclipException +from pytest_mock import MockerFixture +from typer.testing import CliRunner + +from lice.core import app + +runner = CliRunner() + + +class TestCLI: + """Class to test the CLI functionality.""" + + def test_cli_list_licenses(self, mocker: MockerFixture) -> None: + """Test the CLI list_licenses option.""" + mock_list_licenses = mocker.patch("lice.core.list_licenses") + result = runner.invoke(app, ["--licenses"]) + + assert result.exit_code == 0 + mock_list_licenses.assert_called_once() + + def test_cli_list_languages(self, mocker: MockerFixture) -> None: + """Test the CLI list_licenses option.""" + mock_list_languages = mocker.patch("lice.core.list_languages") + result = runner.invoke(app, ["--languages"]) + + assert result.exit_code == 0 + mock_list_languages.assert_called_once() + + def test_cli_generate_header(self, mocker: MockerFixture) -> None: + """Test the CLI generate header option.""" + mock_generate_header = mocker.patch("lice.core.generate_header") + + result = runner.invoke(app, ["--header"]) + + assert result.exit_code == 0 + mock_generate_header.assert_called_once() + + def test_cli_list_vars(self, mocker: MockerFixture) -> None: + """Test the CLI list_vars option.""" + mock_list_vars = mocker.patch("lice.core.list_vars") + + result = runner.invoke(app, ["--vars"]) + + assert result.exit_code == 0 + mock_list_vars.assert_called_once() + + def test_cli_template_path(self, mocker: MockerFixture) -> None: + """Test the CLI template_path option.""" + mock_template_path = mocker.patch("lice.core.load_file_template") + mock_template_path.return_value = StringIO("Mocked template content") + + result = runner.invoke(app, ["--template", "template.txt"]) + + assert result.exit_code == 0 + mock_template_path.assert_called_once_with("template.txt") + + def test_cli_write_to_file_with_extension( + self, mocker: MockerFixture + ) -> None: + """Test the CLI write to file option with an extension.""" + mock_open = mocker.mock_open() + mocker.patch("pathlib.Path.open", mock_open) + + result = runner.invoke(app, ["--file", "output.py"]) + + assert result.exit_code == 0 + mock_open.assert_called_with(mode="w") + mock_open().write.assert_called_once() + mock_open().close.assert_called() + + def test_cli_write_to_file_without_extension( + self, mocker: MockerFixture + ) -> None: + """Test the CLI write to file option without extension. + + This test needs improving so we can test the output file name is + generated correctly. + """ + mock_open = mocker.mock_open() + mocker.patch("pathlib.Path.open", mock_open) + + result = runner.invoke(app, ["--file", "output"]) + + assert result.exit_code == 0 + mock_open.assert_called_with(mode="w") + mock_open().write.assert_called_once() + mock_open().close.assert_called() + + def test_cli_write_to_clipboard(self, mocker: MockerFixture) -> None: + """Test the CLI write to clipboard option.""" + mock_clipboard = mocker.patch("pyperclip.copy") + + result = runner.invoke(app, ["--clipboard"]) + + assert result.exit_code == 0 + assert "License text copied to clipboard" in result.output + mock_clipboard.assert_called_once() + + def test_cli_write_to_clipboard_error(self, mocker: MockerFixture) -> None: + """Test the CLI write to clipboard option with an error.""" + mocker.patch("pyperclip.copy", side_effect=PyperclipException) + + result = runner.invoke(app, ["--clipboard"]) + + assert result.exit_code == 2 # noqa: PLR2004 + assert "Error copying to clipboard" in result.output + + def test_cli_version_command(self) -> None: + """Test the CLI version command.""" + result = runner.invoke(app, ["--version"]) + + assert result.exit_code == 0 + assert "Lice" in result.output + assert "Version" in result.output + + def test_metadata_command(self) -> None: + """Test the CLI metadata command. + + We already test the actual JSON output in the unit tests, so we just + check that the command runs successfully here. + """ + result = runner.invoke(app, ["--metadata"]) + + assert result.exit_code == 0 + + assert '"licenses":' in result.output + assert '"languages":' in result.output + + def test_cli_smoke_test_license_generation(self) -> None: + """Test multiple CLI opotions.""" + result = runner.invoke( + app, ["mit", "--org", "Test Org", "--year", "2024"] + ) + assert result.exit_code == 0 + assert "MIT License" in result.output + assert "Copyright (c) 2024 Test Org" in result.output diff --git a/lice/tests/test_lice.py b/lice/tests/test_lice.py index 70ee0de..1da7ac3 100644 --- a/lice/tests/test_lice.py +++ b/lice/tests/test_lice.py @@ -1,83 +1,549 @@ +"""Test suite for the application.""" + +import io +import json import os +import subprocess from io import StringIO +from pathlib import Path +from types import SimpleNamespace + +import pytest +import typer +from pyfakefs.fake_filesystem import FakeFilesystem +from pyperclip import PyperclipException +from pytest_mock import MockerFixture import lice -from lice.core import ( - LICENSES, clean_path, extract_vars, generate_license, - load_file_template, load_package_template) +from lice.config import check_default_license +from lice.constants import LANGS, LICENSES +from lice.helpers import ( + clean_path, + extract_vars, + format_license, + generate_header, + generate_license, + get_context, + get_lang, + get_metadata, + get_suffix, + guess_organization, + list_languages, + list_licenses, + list_vars, + load_file_template, + load_package_template, + validate_license, + validate_year, +) +from lice.tests.conftest import TEMPLATE_FILE + +TEMPLATE_PATH = Path(lice.__file__).parent / "templates" + + +class TestLice: + """Test the lice module functionality.""" + + def test_paths(self) -> None: + """Test the 'clean_path' function.""" + assert clean_path(".") == str(Path.cwd()) + assert clean_path("$HOME") == os.environ["HOME"] + assert clean_path("~") == os.environ["HOME"] + + def test_file_template(self) -> None: + """Test we can load templates properly.""" + for license_name in LICENSES: + path = TEMPLATE_PATH / (f"template-{license_name}.txt") + with path.open() as infile: + content = infile.read() + assert content == load_file_template(str(path)).getvalue() + + def test_package_template(self) -> None: + """Test the 'load_package_template' function.""" + for license_name in LICENSES: + path = TEMPLATE_PATH / (f"template-{license_name}.txt") + with path.open() as infile: + assert ( + infile.read() + == load_package_template(license_name).getvalue() + ) + + def test_extract_vars(self) -> None: + """Test the 'extract_vars' function.""" + template = StringIO() + for _ in LICENSES: + template.write("Oh hey, {{ this }} is a {{ template }} test.") + var_list = extract_vars(template) + assert var_list == ["template", "this"] + + def test_generate_license(self) -> None: + """Test the 'generate_license' function.""" + context = { + "year": "1981", + "project": "lice", + "organization": "Awesome Co.", + } + + for license_name in LICENSES: + template = load_package_template(license_name) + content = template.getvalue() + + content = content.replace("{{ year }}", context["year"]) + content = content.replace("{{ project }}", context["project"]) + content = content.replace( + "{{ organization }}", context["organization"] + ) + + assert content == generate_license(template, context).getvalue() + template.close() # discard memory + + def test_license_header(self) -> None: + """Test the license header is correct.""" + context = { + "year": "1981", + "project": "lice", + "organization": "Awesome Co.", + } + + try: + for license_name in LICENSES: + template = load_package_template(license_name, header=True) + content = template.getvalue() + + content = content.replace("{{ year }}", context["year"]) + content = content.replace("{{ project }}", context["project"]) + content = content.replace( + "{{ organization }}", context["organization"] + ) + + assert content == generate_license(template, context).getvalue() + template.close() # discard memory + + except OSError: + pass # it's okay to not find templates + + def test_get_context(self) -> None: + """Test the 'get_context' function.""" + context_dict = { + "year": "2024", + "project": "lice", + "organization": "Awesome Co.", + } + fake_context = SimpleNamespace(**context_dict) + + context = get_context(fake_context) + assert context["year"] == "2024" + assert context["project"] == "lice" + assert context["organization"] == "Awesome Co." + + def test_get_lang(self, args: SimpleNamespace) -> None: + """Test the 'get_lang' function.""" + args.language = "py" + result = get_lang(args) + + assert result == "py" + + def test_get_bad_lang( + self, args: SimpleNamespace, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test the 'get_lang' function with a bad language spec.""" + args.language = "bad" + with pytest.raises(typer.Exit) as exc: + get_lang(args) + + captured = capsys.readouterr() + + assert exc.value.exit_code == 1 + assert ( + "I do not know about a language ending with extension 'bad'." + in captured.err + ) + + def test_list_languages(self, capsys: pytest.CaptureFixture[str]) -> None: + """Test the 'list_languages' function.""" + with pytest.raises(typer.Exit) as exc: + list_languages() + + captured = capsys.readouterr() + + assert exc.value.exit_code == 0 + assert ( + "The following source code formatting languages are supported:" + in captured.out + ) + for lang in LANGS: + assert lang in captured.out + + def test_list_licenses(self, capsys: pytest.CaptureFixture[str]) -> None: + """Test the 'list_licenses' function.""" + with pytest.raises(typer.Exit) as exc: + list_licenses() + + captured = capsys.readouterr() + + assert exc.value.exit_code == 0 + assert "Available Licenses" in captured.out + for license_name in LICENSES: + assert license_name in captured.out + template = load_package_template(license_name) + var_list = extract_vars(template) + for var in var_list: + assert var in captured.out + + def test_get_suffix(self) -> None: + """Test the 'get_suffix' function.""" + assert get_suffix("file.py") == "py" + assert get_suffix("file") is None + assert get_suffix("file.") is None + assert get_suffix("file.unknown") is None + + def test_validate_year(self) -> None: + """Test the 'validate_year' function.""" + assert validate_year("2024") == "2024" + + with pytest.raises(typer.BadParameter) as exc1: + validate_year("12345") + + with pytest.raises(typer.BadParameter) as exc2: + validate_year("123") + + assert "Must be a four-digit year" in exc1.value.message + assert "Must be a four-digit year" in exc2.value.message + + def test_validate_license(self) -> None: + """Test the 'validate_license' function.""" + assert validate_license("mit") == "mit" + + with pytest.raises(typer.BadParameter) as exc: + validate_license("bad") + + assert "License 'bad' not found" in exc.value.message + + def test_list_vars_mit( + self, args: SimpleNamespace, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test the 'list_vars' function for a function with context vars.""" + with pytest.raises(typer.Exit) as exc: + list_vars(args, "mit") + + captured = capsys.readouterr() + + assert exc.value.exit_code == 0 + assert ( + "The mit license template contains the following variables and " + "defaults:" in captured.out + ) + assert "year" in captured.out + assert "organization" in captured.out + + def test_list_vars_gpl3( + self, args: SimpleNamespace, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test the 'list_vars' function for license with NO context vars.""" + with pytest.raises(typer.Exit) as exc: + list_vars(args, "gpl3") + + captured = capsys.readouterr() + + assert exc.value.exit_code == 0 + assert "The gpl3 license template contains no variables" in captured.out + + def test_list_vars_not_in_context( + self, + args: SimpleNamespace, + capsys: pytest.CaptureFixture[str], + mocker: MockerFixture, + ) -> None: + """Test the 'list_vars' function with a variable NOT in the context. + + In this case, we should see the name but not the value. + """ + mock_context = mocker.patch("lice.helpers.get_context") + mock_context.return_value = {"year": "2024"} + with pytest.raises(typer.Exit) as exc: + list_vars(args, "mit") -TEMPLATE_PATH = os.path.dirname(lice.__file__) + captured = capsys.readouterr() + assert exc.value.exit_code == 0 + assert ( + "The mit license template contains the following variables and " + "defaults:" in captured.out + ) + assert "year" in captured.out + assert "2024" in captured.out + assert "organization" in captured.out + # this was removed from context when we mocked it + assert "Awesome Co." not in captured.out -def test_paths(): - assert clean_path(".") == os.getcwd() - assert clean_path("$HOME") == os.environ["HOME"] - assert clean_path("~") == os.environ["HOME"] + def test_list_vars_from_template( + self, args: SimpleNamespace, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test the 'list_vars' function with a template path.""" + args.template_path = Path.home() / "template.txt" + with pytest.raises(typer.Exit) as exc: + list_vars(args, "mit") -def test_file_template(): - for license in LICENSES: - path = os.path.join(TEMPLATE_PATH, "template-%s.txt" % license) - with open(path) as infile: - content = infile.read() - assert content == load_file_template(path).getvalue() + captured = capsys.readouterr() -def test_package_template(): - for license in LICENSES: - path = os.path.join(TEMPLATE_PATH, "template-%s.txt" % license) - with open(path) as infile: - assert infile.read() == load_package_template(license).getvalue() + assert exc.value.exit_code == 0 + assert ( + f"The {args.template_path} license template contains the following " + "variables and defaults:" in captured.out + ) + assert "year" in captured.out + assert "2024" in captured.out + assert "organization" in captured.out + assert "Awesome Co." in captured.out -def test_extract_vars(): - template = StringIO() - for license in LICENSES: - template.write(u'Oh hey, {{ this }} is a {{ template }} test.') - var_list = extract_vars(template) - assert var_list == ["template", "this"] + def test_generate_header_none( + self, args: SimpleNamespace, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test the 'generate_header' function.""" + with pytest.raises(typer.Exit) as exc: + generate_header(args, "py") -def test_license(): + captured = capsys.readouterr() - context = { - "year": u'1981', - "project": u'lice', - "organization": u'Awesome Co.' - } + assert exc.value.exit_code == 1 + assert "Sorry, no source headers are available for mit." in captured.err - for license in LICENSES: + def test_generate_header_exists( + self, args: SimpleNamespace, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test the 'generate_header' function.""" + args.license = "apache" - template = load_package_template(license) - content = template.getvalue() + with pytest.raises(typer.Exit) as exc: + generate_header(args, "py") - content = content.replace(u'{{ year }}', context["year"]) - content = content.replace(u'{{ project }}', context["project"]) - content = content.replace(u'{{ organization }}', - context["organization"]) + captured = capsys.readouterr() - assert content == generate_license(template, context).getvalue() - template.close() # discard memory + assert exc.value.exit_code == 0 -def test_license_header(): + assert "# Copyright 2024 Awesome Co." in captured.out + assert ( + "# Licensed under the Apache License, Version 2.0" in captured.out + ) - context = { - "year": u'1981', - "project": u'lice', - "organization": u'Awesome Co.' - } + def test_generate_header_from_template( + self, args: SimpleNamespace, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test the 'generate_header' function.""" + args.template_path = Path.home() / "template.txt" - for license in LICENSES: + with pytest.raises(typer.Exit) as exc: + generate_header(args, "py") + + captured = capsys.readouterr() + + assert exc.value.exit_code == 0 + + assert "# This is a template file." in captured.out + + assert "# Awesome Co. is the organization." in captured.out + assert "# my_project is the project." in captured.out + assert "# 2024 is the year." in captured.out + + def test_generate_header_to_clipboard( + self, args: SimpleNamespace, mocker: MockerFixture + ) -> None: + """Test the 'generate_header' function with clipboard=True.""" + args.license = "apache" + args.clipboard = True + + mock_pyperclip = mocker.patch("pyperclip.copy") + + with pytest.raises(typer.Exit) as exc: + generate_header(args, "py") + + assert exc.value.exit_code == 0 + mock_pyperclip.assert_called_once() + + def test_generate_header_to_clipboard_fail( + self, + args: SimpleNamespace, + mocker: MockerFixture, + capsys: pytest.CaptureFixture[str], + ) -> None: + """Test the 'generate_header' function with pyperclip exeception.""" + args.license = "apache" + args.clipboard = True + + mocker.patch("pyperclip.copy", side_effect=PyperclipException) + + with pytest.raises(typer.Exit) as exc: + generate_header(args, "py") + + assert exc.value.exit_code == 2 # noqa: PLR2004 + + captured = capsys.readouterr() + assert "Error copying to clipboard" in captured.out + + def test_generate_license_missing_context( + self, mocker: MockerFixture + ) -> None: + """Test the 'generate_license' function with missing context.""" + # Mock the input template StringIO + mock_template = io.StringIO( + "la la la {{ year }} la la la {{ organization }} la more" + ) + + # Mock StringIO for the output to avoid writing to an actual file + mock_out = mocker.patch("io.StringIO", autospec=True) + mock_out_instance = mock_out.return_value + + with pytest.raises( + ValueError, match="missing from the template context" + ): + generate_license(mock_template, {"year": "2024"}) + + mock_out_instance.write.assert_not_called() + + def test_format_license_no_lang_legacy( + self, fake_config: FakeFilesystem + ) -> None: + """Test the 'format_license' function with no lang and legacy=True.""" + content = StringIO(TEMPLATE_FILE) + result = format_license(content, "", legacy=True) + + # Adjust the TEMPLATE_FILE to match the expected output with a leading + # space on each line and leading/post . This extra space is added + # when the '--legacy' flag is used, to maintain compatibility with the + # original lice if required. + adjusted_template = ( + "\n" + + ( + "\n".join(" " + line for line in TEMPLATE_FILE.splitlines()) + + "\n" + ) + + "\n" + ) + + assert result.getvalue() == adjusted_template + + def test_format_license_no_lang(self) -> None: + """Test the 'format_license' function.""" + content = StringIO(TEMPLATE_FILE) + result = format_license(content, "") + + assert result.getvalue() == TEMPLATE_FILE + + def test_format_license_empty_lines(self) -> None: + """Test the 'format_license' function with empty lines.""" + content = StringIO("\n\nThis is a test.\n") + result = format_license(content, "py") + + expected = "#\n#\n# This is a test.\n" + + assert result.getvalue() == expected + + def test_load_file_template_path_not_found(self) -> None: + """Test the 'load_file_template' function with a bad path.""" + with pytest.raises(ValueError, match="path does not exist"): + load_file_template("bad/path/to/template.txt") + + def test_guess_organization_from_config( + self, mocker: MockerFixture + ) -> None: + """Test the 'guess_organization' function. + + Testing when the organization is read from the config file. + """ + mocker.patch("lice.helpers.settings.organization", "Awesome Co.") + result = guess_organization() + assert result == "Awesome Co." + + def test_guess_organization_from_git(self, mocker: MockerFixture) -> None: + """Test the 'guess_organization' function. + + Testing when the organization is read from git. + """ + # Mock the settings.organization to be None or empty + mocker.patch("lice.helpers.settings", organization=None) + + # Mock subprocess.check_output to return a specific git user.name + mock_subprocess = mocker.patch("subprocess.check_output") + mock_subprocess.return_value = b"Mocked Git User" + + # Call the function under test + result = guess_organization() + + # Assert that the function returns the git user.name + assert result == "Mocked Git User" + + def test_guess_organization_from_user(self, mocker: MockerFixture) -> None: + """Test the 'guess_organization' function. + + Testing when the organization is read from the $USER environment + variable. + """ + # Mock the settings.organization to be None or empty + mocker.patch("lice.helpers.settings", organization=None) + + # Mock subprocess.check_output to raise a CalledProcessError + mock_subprocess = mocker.patch("subprocess.check_output") + mock_subprocess.side_effect = subprocess.CalledProcessError( + 1, "git config --get user.name" + ) + + # Mock getpass.getuser to return a specific username + mock_getuser = mocker.patch("getpass.getuser") + mock_getuser.return_value = "Mocked User" + + # Call the function under test + result = guess_organization() + + # Assert that the function falls back to the username + assert result == "Mocked User" + + def test_bad_default_license( + self, mocker: MockerFixture, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test when the config file has a bad default license. + + It should return bsd3 instead, and not raise an exception. + It should also print a warning message. + """ + mocker.patch("lice.config.settings", default_license="bad") + + result = check_default_license() + + captured = capsys.readouterr() + + assert result == "bsd3" + + assert ( + "Invalid default license 'bad' in the configuration file" + in captured.out + ) + + def test_get_metadata_is_valid_json( + self, args: SimpleNamespace, capsys: pytest.CaptureFixture[str] + ) -> None: + """Test the 'get_metadata' function.""" + licenses = json.dumps(LICENSES) + languages = json.dumps(list(LANGS.keys())) + + with pytest.raises(typer.Exit) as exc: + get_metadata(args) + + captured = capsys.readouterr() try: + json_result = json.loads(captured.out) + except json.JSONDecodeError: + pytest.fail("The output is not valid JSON.") - template = load_package_template(license, header=True) - content = template.getvalue() + assert exc.value.exit_code == 0 - content = content.replace(u'{{ year }}', context["year"]) - content = content.replace(u'{{ project }}', context["project"]) - content = content.replace(u'{{ organization }}', - context["organization"]) + json_keys = json_result.keys() - assert content == generate_license(template, context).getvalue() - template.close() # discard memory + for key in ["organization", "project", "licenses", "languages"]: + assert key in json_keys - except IOError: - pass # it's okay to not find templates + assert json_result["organization"] == "Awesome Co." + assert json_result["project"] == "my_project" + assert licenses in captured.out + assert languages in captured.out diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..7989f42 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,62 @@ +site_name: Lice - Generate your license files with ease + +# default to using the material theme +theme: + name: material + palette: + primary: light-blue + accent: blue + features: + - navigation.footer + - navigation.expand + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/licenses/lice + - icon: fontawesome/brands/twitter + link: https://twitter.com/gnramsay_dev + +copyright: Website © 2024-2025 Grant Ramsay (Seapagan) + +repo_name: lice +repo_url: https://github.com/licenses/lice + +# default plugins here are to minify the html, css and js plus enable the search +# plugin. Adjust to your liking. +plugins: + - search + - minify: + minify_html: true + minify_css: true + minify_js: true + htmlmin_opts: + remove_comments: true + remove_empty_space: true + +# edit these MARKDOWN extensions to your liking +markdown_extensions: + - admonition + - pymdownx.snippets + - pymdownx.superfences + - pymdownx.highlight: + linenums: false + auto_title: false + - attr_list + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + +nav: + - Home: index.md + - Installation: installation.md + - Overview: overview.md + - Usage: usage.md + - Configuration: configuration.md + - Integration: integration.md + - Contributing: contributing.md + - Changelog: changelog.md + - Future Plans: future_plans.md + +extra_css: + - css/extra.css diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f0f8f09 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,154 @@ +[project] +name = "lice" +version = "0.13.0" +description = "Generate license files for your projects" +authors = [ + { name = "Jeremy Carbaugh", email = "jcarbaugh@gmail.com" }, + { name = "Grant Ramsay", email = "grant@gnramsay.com" }, +] +license = "BSD-3-Clause" +readme = "README.md" +requires-python = ">=3.9" + +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dependencies = [ + "pyperclip>=1.9.0", + "rich>=13.8.0", + "simple-toml-settings>=0.8.0", + "single-source>=0.4.0", + "typer>=0.12.5", +] + +[project.urls] +"repository" = "https://github.com/licenses/lice" +# "homepage" = "https://seapagan.github.io/lice2/" +"Pull Requests" = "https://github.com/licenses/lice/pulls" +"Bug Tracker" = "https://github.com/licenses/lice/issues" +"Changelog" = "https://github.com/licenses/lice/blob/main/CHANGELOG.md" + +[project.scripts] +lice = "lice.core:app" + +[tool.uv] +dev-dependencies = [ + "mypy >= 1.11.2", + "pre-commit >= 3.8.0", + "ruff >= 0.7.3", + "pymarkdownlnt >= 0.9.22", + "pyfakefs >= 5.6.0", + "pytest >= 8.3.2", + "pytest-clarity >= 1.0.1", + "pytest-cov >= 5.0.0", + "pytest-sugar >= 1.0.0", + "pytest-randomly >= 3.15.0", + "pytest-reverse >= 1.7.0", + "pytest-mock >= 3.14.0", + "mock >= 5.1.0", + "poethepoet >= 0.28.0", + "github-changelog-md >= 0.9.5", + "mkdocs >= 1.6.1", + "mkdocs-autorefs >= 1.2.0", + "mkdocs-material >= 9.5.34", + "mkdocs-minify-plugin >= 0.8.0", + "pymdown-extensions >= 10.9", + "pygments >= 2.18.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + + +[tool.poe.tasks] +pre.cmd = "pre-commit run --all-files" +pre.help = "Run pre-commit checks" +mypy.cmd = "mypy . --strict" +mypy.help = "Run mypy checks" +format.help = "Format code with Ruff" +format.cmd = "ruff format ." +ruff.help = "Run Ruff checks" +ruff.cmd = "ruff check --output-format=concise ." + +changelog.cmd = "github-changelog-md" +changelog.help = "Generate a changelog" + +"docs:publish".cmd = "mkdocs gh-deploy" +"docs:publish".help = "Publish documentation to GitHub Pages" +"docs:build".cmd = "mkdocs build" +"docs:build".help = "Build documentation locally to './site' folder" +"docs:serve".cmd = "mkdocs serve -w TODO.md -w CHANGELOG.md -w CONTRIBUTING.md" +"docs:serve".help = "Serve documentation locally" +"docs:serve:all".cmd = "mkdocs serve -w TODO.md -w CHANGELOG.md -w CONTRIBUTING.md -a 0.0.0.0:9000" +"docs:serve:all".help = "Serve documentation locally on all interfaces" + +[tool.pytest.ini_options] +addopts = ["--cov", "--cov-report", "term-missing", "--cov-report", "html"] +filterwarnings = [ + "ignore:pkg_resources is deprecated as an API:DeprecationWarning", +] +mock_use_standalone_module = true + +[tool.coverage.run] +source = ["lice"] +omit = ["*/tests/*"] + +[tool.ruff] +line-length = 80 +lint.select = ["ALL"] # we are being very strict! +lint.ignore = [ + "PGH003", + "FBT002", + "FBT003", + "B006", +] # These rules are too strict even for us 😝 +lint.extend-ignore = [ + "COM812", + "ISC001", +] # these are ignored for ruff formatting + +src = ["lice"] +target-version = "py39" # minimum python version supported + +[tool.ruff.format] +indent-style = "space" +quote-style = "double" + +[tool.ruff.lint.pep8-naming] +classmethod-decorators = ["pydantic.validator", "pydantic.root_validator"] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.extend-per-file-ignores] +"lice/tests/**/*.py" = [ + "S101", # we can (and MUST!) use 'assert' in test files. + "ANN001", # annotations for fixtures are sometimes a pain for test files + "ARG00", # test fixtures often are not directly used +] + +[tool.ruff.lint.isort] +known-first-party = ["lice"] + +[tool.ruff.lint.pyupgrade] +keep-runtime-typing = true + +[tool.mypy] +python_version = "3.9" + +[[tool.mypy.overrides]] +module = "pyperclip" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +disable_error_code = ["method-assign", "no-untyped-def", "attr-defined"] +module = "tests.*" diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..6b5ac68 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,90 @@ +# This file was autogenerated by uv via the following command: +# uv export --no-hashes --no-emit-project --output-file=requirements-dev.txt +application-properties==0.8.2 +babel==2.16.0 +certifi==2024.8.30 +cffi==1.17.1 +cfgv==3.4.0 +charset-normalizer==3.4.0 +click==8.1.7 +colorama==0.4.6 +columnar==1.4.1 +coverage==7.6.4 +cryptography==43.0.3 +csscompressor==0.9.5 +deprecated==1.2.14 +distlib==0.3.9 +exceptiongroup==1.2.2 ; python_full_version < '3.11' +filelock==3.16.1 +ghp-import==2.1.0 +github-changelog-md==0.9.5 +htmlmin2==0.1.13 +identify==2.6.2 +idna==3.10 +importlib-metadata==8.5.0 ; python_full_version < '3.10' +iniconfig==2.0.0 +jinja2==3.1.4 +jsmin==3.0.1 +markdown==3.7 +markdown-it-py==3.0.0 +markupsafe==3.0.2 +mdurl==0.1.2 +mergedeep==1.3.4 +mkdocs==1.6.1 +mkdocs-autorefs==1.2.0 +mkdocs-get-deps==0.2.0 +mkdocs-material==9.5.44 +mkdocs-material-extensions==1.3.1 +mkdocs-minify-plugin==0.8.0 +mock==5.1.0 +mypy==1.13.0 +mypy-extensions==1.0.0 +nodeenv==1.9.1 +packaging==24.2 +paginate==0.5.7 +pastel==0.2.1 +pathspec==0.12.1 +platformdirs==4.3.6 +pluggy==1.5.0 +poethepoet==0.30.0 +pprintpp==0.4.0 +pre-commit==4.0.1 +pycparser==2.22 +pyfakefs==5.7.1 +pygithub==2.5.0 +pygments==2.18.0 +pyjwt==2.9.0 +pymarkdownlnt==0.9.25 +pymdown-extensions==10.12 +pynacl==1.5.0 +pyperclip==1.9.0 +pytest==8.3.3 +pytest-clarity==1.0.1 +pytest-cov==6.0.0 +pytest-mock==3.14.0 +pytest-randomly==3.16.0 +pytest-reverse==1.8.0 +pytest-sugar==1.0.0 +python-dateutil==2.9.0.post0 +pyyaml==6.0.2 +pyyaml-env-tag==0.1 +regex==2024.11.6 +requests==2.32.3 +rich==13.9.4 +rtoml==0.12.0 +ruff==0.7.3 +shellingham==1.5.4 +simple-toml-settings==0.9.0 +single-source==0.4.0 +six==1.16.0 +termcolor==2.5.0 +tomli==2.1.0 +toolz==1.0.0 +typer==0.12.5 +typing-extensions==4.12.2 +urllib3==2.2.3 +virtualenv==20.27.1 +watchdog==6.0.0 +wcwidth==0.2.13 +wrapt==1.16.0 +zipp==3.21.0 ; python_full_version < '3.10' diff --git a/requirements.txt b/requirements.txt index 036d8c5..53a7a0c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,15 @@ -pytest -flake8 \ No newline at end of file +# This file was autogenerated by uv via the following command: +# uv export --no-hashes --no-dev --no-emit-project --output-file=requirements.txt +click==8.1.7 +colorama==0.4.6 ; sys_platform == 'win32' +markdown-it-py==3.0.0 +mdurl==0.1.2 +pygments==2.18.0 +pyperclip==1.9.0 +rich==13.9.4 +rtoml==0.12.0 +shellingham==1.5.4 +simple-toml-settings==0.9.0 +single-source==0.4.0 +typer==0.12.5 +typing-extensions==4.12.2 diff --git a/setup.py b/setup.py deleted file mode 100644 index 9e4cef3..0000000 --- a/setup.py +++ /dev/null @@ -1,36 +0,0 @@ -from lice import __version__ -from setuptools import setup, find_packages -import sys - -long_description = open('README.rst').read() - -extra_kwargs = {} -if sys.version_info >= (3,): - extra_kwargs['setup_requires'] = ['setuptools'] - -setup( - name="lice", - version=__version__, - author="Jeremy Carbaugh", - author_email="jcarbaugh@gmail.com", - url='https://github.com/licenses/lice', - description='Generate a license file for a project', - long_description=long_description, - license='BSD', - packages=find_packages(), - package_data={'lice': ['*.txt']}, - include_package_data=True, - entry_points={ - 'console_scripts': ['lice = lice:main']}, - platforms=['any'], - test_suite='lice.tests.collector', - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: BSD License', - 'Intended Audience :: Developers', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - ], - **extra_kwargs -) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..86b45ec --- /dev/null +++ b/uv.lock @@ -0,0 +1,1626 @@ +version = 1 +requires-python = ">=3.9" + +[[package]] +name = "application-properties" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, + { name = "tomli" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/5e/29ec0fa553ee5befc6a1e47eb0c8ffea75eed941251524678700e2d3e747/application_properties-0.8.2.tar.gz", hash = "sha256:e5e6918c8e29ab57175567d51dfa39c00a1d75b3205625559bb02250f50f0420", size = 29595 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/27/ea2b77232385ec1c4b5cb766ee13b5a3085ed2fa789d61374e7af36b79e1/application_properties-0.8.2-py3-none-any.whl", hash = "sha256:a4fe684e4d95fc45054d3316acf763a7b0f29342ccea02eee09de53004f0139c", size = 18399 }, +] + +[[package]] +name = "babel" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, + { url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220 }, + { url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605 }, + { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910 }, + { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200 }, + { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565 }, + { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635 }, + { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218 }, + { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486 }, + { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911 }, + { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632 }, + { url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820 }, + { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290 }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/8b/825cc84cf13a28bfbcba7c416ec22bf85a9584971be15b21dd8300c65b7f/charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", size = 196363 }, + { url = "https://files.pythonhosted.org/packages/23/81/d7eef6a99e42c77f444fdd7bc894b0ceca6c3a95c51239e74a722039521c/charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", size = 125639 }, + { url = "https://files.pythonhosted.org/packages/21/67/b4564d81f48042f520c948abac7079356e94b30cb8ffb22e747532cf469d/charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", size = 120451 }, + { url = "https://files.pythonhosted.org/packages/c2/72/12a7f0943dd71fb5b4e7b55c41327ac0a1663046a868ee4d0d8e9c369b85/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", size = 140041 }, + { url = "https://files.pythonhosted.org/packages/67/56/fa28c2c3e31217c4c52158537a2cf5d98a6c1e89d31faf476c89391cd16b/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", size = 150333 }, + { url = "https://files.pythonhosted.org/packages/f9/d2/466a9be1f32d89eb1554cf84073a5ed9262047acee1ab39cbaefc19635d2/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", size = 142921 }, + { url = "https://files.pythonhosted.org/packages/f8/01/344ec40cf5d85c1da3c1f57566c59e0c9b56bcc5566c08804a95a6cc8257/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", size = 144785 }, + { url = "https://files.pythonhosted.org/packages/73/8b/2102692cb6d7e9f03b9a33a710e0164cadfce312872e3efc7cfe22ed26b4/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", size = 146631 }, + { url = "https://files.pythonhosted.org/packages/d8/96/cc2c1b5d994119ce9f088a9a0c3ebd489d360a2eb058e2c8049f27092847/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", size = 140867 }, + { url = "https://files.pythonhosted.org/packages/c9/27/cde291783715b8ec30a61c810d0120411844bc4c23b50189b81188b273db/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", size = 149273 }, + { url = "https://files.pythonhosted.org/packages/3a/a4/8633b0fc1a2d1834d5393dafecce4a1cc56727bfd82b4dc18fc92f0d3cc3/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", size = 152437 }, + { url = "https://files.pythonhosted.org/packages/64/ea/69af161062166b5975ccbb0961fd2384853190c70786f288684490913bf5/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", size = 150087 }, + { url = "https://files.pythonhosted.org/packages/3b/fd/e60a9d9fd967f4ad5a92810138192f825d77b4fa2a557990fd575a47695b/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", size = 145142 }, + { url = "https://files.pythonhosted.org/packages/6d/02/8cb0988a1e49ac9ce2eed1e07b77ff118f2923e9ebd0ede41ba85f2dcb04/charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", size = 94701 }, + { url = "https://files.pythonhosted.org/packages/d6/20/f1d4670a8a723c46be695dff449d86d6092916f9e99c53051954ee33a1bc/charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", size = 102191 }, + { url = "https://files.pythonhosted.org/packages/9c/61/73589dcc7a719582bf56aae309b6103d2762b526bffe189d635a7fcfd998/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", size = 193339 }, + { url = "https://files.pythonhosted.org/packages/77/d5/8c982d58144de49f59571f940e329ad6e8615e1e82ef84584c5eeb5e1d72/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", size = 124366 }, + { url = "https://files.pythonhosted.org/packages/bf/19/411a64f01ee971bed3231111b69eb56f9331a769072de479eae7de52296d/charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", size = 118874 }, + { url = "https://files.pythonhosted.org/packages/4c/92/97509850f0d00e9f14a46bc751daabd0ad7765cff29cdfb66c68b6dad57f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", size = 138243 }, + { url = "https://files.pythonhosted.org/packages/e2/29/d227805bff72ed6d6cb1ce08eec707f7cfbd9868044893617eb331f16295/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", size = 148676 }, + { url = "https://files.pythonhosted.org/packages/13/bc/87c2c9f2c144bedfa62f894c3007cd4530ba4b5351acb10dc786428a50f0/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", size = 141289 }, + { url = "https://files.pythonhosted.org/packages/eb/5b/6f10bad0f6461fa272bfbbdf5d0023b5fb9bc6217c92bf068fa5a99820f5/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", size = 142585 }, + { url = "https://files.pythonhosted.org/packages/3b/a0/a68980ab8a1f45a36d9745d35049c1af57d27255eff8c907e3add84cf68f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", size = 144408 }, + { url = "https://files.pythonhosted.org/packages/d7/a1/493919799446464ed0299c8eef3c3fad0daf1c3cd48bff9263c731b0d9e2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", size = 139076 }, + { url = "https://files.pythonhosted.org/packages/fb/9d/9c13753a5a6e0db4a0a6edb1cef7aee39859177b64e1a1e748a6e3ba62c2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", size = 146874 }, + { url = "https://files.pythonhosted.org/packages/75/d2/0ab54463d3410709c09266dfb416d032a08f97fd7d60e94b8c6ef54ae14b/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", size = 150871 }, + { url = "https://files.pythonhosted.org/packages/8d/c9/27e41d481557be53d51e60750b85aa40eaf52b841946b3cdeff363105737/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", size = 148546 }, + { url = "https://files.pythonhosted.org/packages/ee/44/4f62042ca8cdc0cabf87c0fc00ae27cd8b53ab68be3605ba6d071f742ad3/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", size = 143048 }, + { url = "https://files.pythonhosted.org/packages/01/f8/38842422988b795220eb8038745d27a675ce066e2ada79516c118f291f07/charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", size = 94389 }, + { url = "https://files.pythonhosted.org/packages/0b/6e/b13bd47fa9023b3699e94abf565b5a2f0b0be6e9ddac9812182596ee62e4/charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", size = 101752 }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4b7a70987abf9b8196845806198975b6aab4ce016632f817ad758a5aa056/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", size = 194445 }, + { url = "https://files.pythonhosted.org/packages/50/89/354cc56cf4dd2449715bc9a0f54f3aef3dc700d2d62d1fa5bbea53b13426/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", size = 125275 }, + { url = "https://files.pythonhosted.org/packages/fa/44/b730e2a2580110ced837ac083d8ad222343c96bb6b66e9e4e706e4d0b6df/charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", size = 119020 }, + { url = "https://files.pythonhosted.org/packages/9d/e4/9263b8240ed9472a2ae7ddc3e516e71ef46617fe40eaa51221ccd4ad9a27/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", size = 139128 }, + { url = "https://files.pythonhosted.org/packages/6b/e3/9f73e779315a54334240353eaea75854a9a690f3f580e4bd85d977cb2204/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", size = 149277 }, + { url = "https://files.pythonhosted.org/packages/1a/cf/f1f50c2f295312edb8a548d3fa56a5c923b146cd3f24114d5adb7e7be558/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", size = 142174 }, + { url = "https://files.pythonhosted.org/packages/16/92/92a76dc2ff3a12e69ba94e7e05168d37d0345fa08c87e1fe24d0c2a42223/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", size = 143838 }, + { url = "https://files.pythonhosted.org/packages/a4/01/2117ff2b1dfc61695daf2babe4a874bca328489afa85952440b59819e9d7/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", size = 146149 }, + { url = "https://files.pythonhosted.org/packages/f6/9b/93a332b8d25b347f6839ca0a61b7f0287b0930216994e8bf67a75d050255/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", size = 140043 }, + { url = "https://files.pythonhosted.org/packages/ab/f6/7ac4a01adcdecbc7a7587767c776d53d369b8b971382b91211489535acf0/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", size = 148229 }, + { url = "https://files.pythonhosted.org/packages/9d/be/5708ad18161dee7dc6a0f7e6cf3a88ea6279c3e8484844c0590e50e803ef/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", size = 151556 }, + { url = "https://files.pythonhosted.org/packages/5a/bb/3d8bc22bacb9eb89785e83e6723f9888265f3a0de3b9ce724d66bd49884e/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", size = 149772 }, + { url = "https://files.pythonhosted.org/packages/f7/fa/d3fc622de05a86f30beea5fc4e9ac46aead4731e73fd9055496732bcc0a4/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", size = 144800 }, + { url = "https://files.pythonhosted.org/packages/9a/65/bdb9bc496d7d190d725e96816e20e2ae3a6fa42a5cac99c3c3d6ff884118/charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", size = 94836 }, + { url = "https://files.pythonhosted.org/packages/3e/67/7b72b69d25b89c0b3cea583ee372c43aa24df15f0e0f8d3982c57804984b/charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", size = 102187 }, + { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, + { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, + { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, + { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, + { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, + { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, + { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, + { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, + { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, + { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, + { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, + { url = "https://files.pythonhosted.org/packages/54/2f/28659eee7f5d003e0f5a3b572765bf76d6e0fe6601ab1f1b1dd4cba7e4f1/charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", size = 196326 }, + { url = "https://files.pythonhosted.org/packages/d1/18/92869d5c0057baa973a3ee2af71573be7b084b3c3d428fe6463ce71167f8/charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", size = 125614 }, + { url = "https://files.pythonhosted.org/packages/d6/27/327904c5a54a7796bb9f36810ec4173d2df5d88b401d2b95ef53111d214e/charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", size = 120450 }, + { url = "https://files.pythonhosted.org/packages/a4/23/65af317914a0308495133b2d654cf67b11bbd6ca16637c4e8a38f80a5a69/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", size = 140135 }, + { url = "https://files.pythonhosted.org/packages/f2/41/6190102ad521a8aa888519bb014a74251ac4586cde9b38e790901684f9ab/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", size = 150413 }, + { url = "https://files.pythonhosted.org/packages/7b/ab/f47b0159a69eab9bd915591106859f49670c75f9a19082505ff16f50efc0/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", size = 142992 }, + { url = "https://files.pythonhosted.org/packages/28/89/60f51ad71f63aaaa7e51a2a2ad37919985a341a1d267070f212cdf6c2d22/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", size = 144871 }, + { url = "https://files.pythonhosted.org/packages/0c/48/0050550275fea585a6e24460b42465020b53375017d8596c96be57bfabca/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", size = 146756 }, + { url = "https://files.pythonhosted.org/packages/dc/b5/47f8ee91455946f745e6c9ddbb0f8f50314d2416dd922b213e7d5551ad09/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", size = 141034 }, + { url = "https://files.pythonhosted.org/packages/84/79/5c731059ebab43e80bf61fa51666b9b18167974b82004f18c76378ed31a3/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", size = 149434 }, + { url = "https://files.pythonhosted.org/packages/ca/f3/0719cd09fc4dc42066f239cb3c48ced17fc3316afca3e2a30a4756fe49ab/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", size = 152443 }, + { url = "https://files.pythonhosted.org/packages/f7/0e/c6357297f1157c8e8227ff337e93fd0a90e498e3d6ab96b2782204ecae48/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", size = 150294 }, + { url = "https://files.pythonhosted.org/packages/54/9a/acfa96dc4ea8c928040b15822b59d0863d6e1757fba8bd7de3dc4f761c13/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", size = 145314 }, + { url = "https://files.pythonhosted.org/packages/73/1c/b10a63032eaebb8d7bcb8544f12f063f41f5f463778ac61da15d9985e8b6/charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", size = 94724 }, + { url = "https://files.pythonhosted.org/packages/c5/77/3a78bf28bfaa0863f9cfef278dbeadf55efe064eafff8c7c424ae3c4c1bf/charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", size = 102159 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "columnar" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "toolz" }, + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/0d/a0b2fd781050d29c9df64ac6df30b5f18b775724b79779f56fc5a8298fe9/Columnar-1.4.1.tar.gz", hash = "sha256:c3cb57273333b2ff9cfaafc86f09307419330c97faa88dcfe23df05e6fbb9c72", size = 11386 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/00/a17a5657bf090b9dffdb310ac273c553a38f9252f60224da9fe62d9b60e9/Columnar-1.4.1-py3-none-any.whl", hash = "sha256:8efb692a7e6ca07dcc8f4ea889960421331a5dffa8e5af81f0a67ad8ea1fc798", size = 11845 }, +] + +[[package]] +name = "coverage" +version = "7.6.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/12/3669b6382792783e92046730ad3327f53b2726f0603f4c311c4da4824222/coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", size = 798716 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/93/4ad92f71e28ece5c0326e5f4a6630aa4928a8846654a65cfff69b49b95b9/coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", size = 206713 }, + { url = "https://files.pythonhosted.org/packages/01/ae/747a580b1eda3f2e431d87de48f0604bd7bc92e52a1a95185a4aa585bc47/coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", size = 207149 }, + { url = "https://files.pythonhosted.org/packages/07/1a/1f573f8a6145f6d4c9130bbc120e0024daf1b24cf2a78d7393fa6eb6aba7/coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", size = 235584 }, + { url = "https://files.pythonhosted.org/packages/40/42/c8523f2e4db34aa9389caee0d3688b6ada7a84fcc782e943a868a7f302bd/coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/8d/95/565c310fffa16ede1a042e9ea1ca3962af0d8eb5543bc72df6b91dc0c3d5/coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", size = 234649 }, + { url = "https://files.pythonhosted.org/packages/d5/81/3b550674d98968ec29c92e3e8650682be6c8b1fa7581a059e7e12e74c431/coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", size = 233744 }, + { url = "https://files.pythonhosted.org/packages/0d/70/d66c7f51b3e33aabc5ea9f9624c1c9d9655472962270eb5e7b0d32707224/coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", size = 232204 }, + { url = "https://files.pythonhosted.org/packages/23/2d/2b3a2dbed7a5f40693404c8a09e779d7c1a5fbed089d3e7224c002129ec8/coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", size = 233335 }, + { url = "https://files.pythonhosted.org/packages/5a/4f/92d1d2ad720d698a4e71c176eacf531bfb8e0721d5ad560556f2c484a513/coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", size = 209435 }, + { url = "https://files.pythonhosted.org/packages/c7/b9/cdf158e7991e2287bcf9082670928badb73d310047facac203ff8dcd5ff3/coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", size = 210243 }, + { url = "https://files.pythonhosted.org/packages/87/31/9c0cf84f0dfcbe4215b7eb95c31777cdc0483c13390e69584c8150c85175/coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", size = 206819 }, + { url = "https://files.pythonhosted.org/packages/53/ed/a38401079ad320ad6e054a01ec2b61d270511aeb3c201c80e99c841229d5/coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", size = 207263 }, + { url = "https://files.pythonhosted.org/packages/20/e7/c3ad33b179ab4213f0d70da25a9c214d52464efa11caeab438592eb1d837/coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", size = 239205 }, + { url = "https://files.pythonhosted.org/packages/36/91/fc02e8d8e694f557752120487fd982f654ba1421bbaa5560debf96ddceda/coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", size = 236612 }, + { url = "https://files.pythonhosted.org/packages/cc/57/cb08f0eda0389a9a8aaa4fc1f9fec7ac361c3e2d68efd5890d7042c18aa3/coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", size = 238479 }, + { url = "https://files.pythonhosted.org/packages/d5/c9/2c7681a9b3ca6e6f43d489c2e6653a53278ed857fd6e7010490c307b0a47/coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", size = 237405 }, + { url = "https://files.pythonhosted.org/packages/b5/4e/ebfc6944b96317df8b537ae875d2e57c27b84eb98820bc0a1055f358f056/coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", size = 236038 }, + { url = "https://files.pythonhosted.org/packages/13/f2/3a0bf1841a97c0654905e2ef531170f02c89fad2555879db8fe41a097871/coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", size = 236812 }, + { url = "https://files.pythonhosted.org/packages/b9/9c/66bf59226b52ce6ed9541b02d33e80a6e816a832558fbdc1111a7bd3abd4/coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", size = 209400 }, + { url = "https://files.pythonhosted.org/packages/2a/a0/b0790934c04dfc8d658d4a62acb8f7ca0efdf3818456fcad757b11c6479d/coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", size = 210243 }, + { url = "https://files.pythonhosted.org/packages/7d/e7/9291de916d084f41adddfd4b82246e68d61d6a75747f075f7e64628998d2/coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", size = 207013 }, + { url = "https://files.pythonhosted.org/packages/27/03/932c2c5717a7fa80cd43c6a07d3177076d97b79f12f40f882f9916db0063/coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", size = 207251 }, + { url = "https://files.pythonhosted.org/packages/d5/3f/0af47dcb9327f65a45455fbca846fe96eb57c153af46c4754a3ba678938a/coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", size = 240268 }, + { url = "https://files.pythonhosted.org/packages/8a/3c/37a9d81bbd4b23bc7d46ca820e16174c613579c66342faa390a271d2e18b/coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", size = 237298 }, + { url = "https://files.pythonhosted.org/packages/c0/70/6b0627e5bd68204ee580126ed3513140b2298995c1233bd67404b4e44d0e/coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", size = 239367 }, + { url = "https://files.pythonhosted.org/packages/3c/eb/634d7dfab24ac3b790bebaf9da0f4a5352cbc125ce6a9d5c6cf4c6cae3c7/coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", size = 238853 }, + { url = "https://files.pythonhosted.org/packages/d9/0d/8e3ed00f1266ef7472a4e33458f42e39492e01a64281084fb3043553d3f1/coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", size = 237160 }, + { url = "https://files.pythonhosted.org/packages/ce/9c/4337f468ef0ab7a2e0887a9c9da0e58e2eada6fc6cbee637a4acd5dfd8a9/coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", size = 238824 }, + { url = "https://files.pythonhosted.org/packages/5e/09/3e94912b8dd37251377bb02727a33a67ee96b84bbbe092f132b401ca5dd9/coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", size = 209639 }, + { url = "https://files.pythonhosted.org/packages/01/69/d4f3a4101171f32bc5b3caec8ff94c2c60f700107a6aaef7244b2c166793/coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", size = 210428 }, + { url = "https://files.pythonhosted.org/packages/c2/4d/2dede4f7cb5a70fb0bb40a57627fddf1dbdc6b9c1db81f7c4dcdcb19e2f4/coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", size = 207039 }, + { url = "https://files.pythonhosted.org/packages/3f/f9/d86368ae8c79e28f1fb458ebc76ae9ff3e8bd8069adc24e8f2fed03c58b7/coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", size = 207298 }, + { url = "https://files.pythonhosted.org/packages/64/c5/b4cc3c3f64622c58fbfd4d8b9a7a8ce9d355f172f91fcabbba1f026852f6/coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", size = 239813 }, + { url = "https://files.pythonhosted.org/packages/8a/86/14c42e60b70a79b26099e4d289ccdfefbc68624d096f4481163085aa614c/coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", size = 236959 }, + { url = "https://files.pythonhosted.org/packages/7f/f8/4436a643631a2fbab4b44d54f515028f6099bfb1cd95b13cfbf701e7f2f2/coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", size = 238950 }, + { url = "https://files.pythonhosted.org/packages/49/50/1571810ddd01f99a0a8be464a4ac8b147f322cd1e8e296a1528984fc560b/coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", size = 238610 }, + { url = "https://files.pythonhosted.org/packages/f3/8c/6312d241fe7cbd1f0cade34a62fea6f333d1a261255d76b9a87074d8703c/coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", size = 236697 }, + { url = "https://files.pythonhosted.org/packages/ce/5f/fef33dfd05d87ee9030f614c857deb6df6556b8f6a1c51bbbb41e24ee5ac/coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", size = 238541 }, + { url = "https://files.pythonhosted.org/packages/a9/64/6a984b6e92e1ea1353b7ffa08e27f707a5e29b044622445859200f541e8c/coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", size = 209707 }, + { url = "https://files.pythonhosted.org/packages/5c/60/ce5a9e942e9543783b3db5d942e0578b391c25cdd5e7f342d854ea83d6b7/coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", size = 210439 }, + { url = "https://files.pythonhosted.org/packages/78/53/6719677e92c308207e7f10561a1b16ab8b5c00e9328efc9af7cfd6fb703e/coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", size = 207784 }, + { url = "https://files.pythonhosted.org/packages/fa/dd/7054928930671fcb39ae6a83bb71d9ab5f0afb733172543ced4b09a115ca/coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", size = 208058 }, + { url = "https://files.pythonhosted.org/packages/b5/7d/fd656ddc2b38301927b9eb3aae3fe827e7aa82e691923ed43721fd9423c9/coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", size = 250772 }, + { url = "https://files.pythonhosted.org/packages/90/d0/eb9a3cc2100b83064bb086f18aedde3afffd7de6ead28f69736c00b7f302/coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", size = 246490 }, + { url = "https://files.pythonhosted.org/packages/45/44/3f64f38f6faab8a0cfd2c6bc6eb4c6daead246b97cf5f8fc23bf3788f841/coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", size = 248848 }, + { url = "https://files.pythonhosted.org/packages/5d/11/4c465a5f98656821e499f4b4619929bd5a34639c466021740ecdca42aa30/coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", size = 248340 }, + { url = "https://files.pythonhosted.org/packages/f1/96/ebecda2d016cce9da812f404f720ca5df83c6b29f65dc80d2000d0078741/coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", size = 246229 }, + { url = "https://files.pythonhosted.org/packages/16/d9/3d820c00066ae55d69e6d0eae11d6149a5ca7546de469ba9d597f01bf2d7/coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", size = 247510 }, + { url = "https://files.pythonhosted.org/packages/8f/c3/4fa1eb412bb288ff6bfcc163c11700ff06e02c5fad8513817186e460ed43/coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", size = 210353 }, + { url = "https://files.pythonhosted.org/packages/7e/77/03fc2979d1538884d921c2013075917fc927f41cd8526909852fe4494112/coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", size = 211502 }, + { url = "https://files.pythonhosted.org/packages/fb/27/7efede2355bd1417137246246ab0980751b3ba6065102518a2d1eba6a278/coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3", size = 206714 }, + { url = "https://files.pythonhosted.org/packages/f3/94/594af55226676d078af72b329372e2d036f9ba1eb6bcf1f81debea2453c7/coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c", size = 207146 }, + { url = "https://files.pythonhosted.org/packages/d5/13/19de1c5315b22795dd67dbd9168281632424a344b648d23d146572e42c2b/coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076", size = 235180 }, + { url = "https://files.pythonhosted.org/packages/db/26/8fba01ce9f376708c7efed2761cea740f50a1b4138551886213797a4cecd/coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376", size = 233100 }, + { url = "https://files.pythonhosted.org/packages/74/66/4db60266551b89e820b457bc3811a3c5eaad3c1324cef7730c468633387a/coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0", size = 234231 }, + { url = "https://files.pythonhosted.org/packages/2a/9b/7b33f0892fccce50fc82ad8da76c7af1731aea48ec71279eef63a9522db7/coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858", size = 233383 }, + { url = "https://files.pythonhosted.org/packages/91/49/6ff9c4e8a67d9014e1c434566e9169965f970350f4792a0246cd0d839442/coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111", size = 231863 }, + { url = "https://files.pythonhosted.org/packages/81/f9/c9d330dec440676b91504fcceebca0814718fa71c8498cf29d4e21e9dbfc/coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901", size = 232854 }, + { url = "https://files.pythonhosted.org/packages/ee/d9/605517a023a0ba8eb1f30d958f0a7ff3a21867b07dcb42618f862695ca0e/coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09", size = 209437 }, + { url = "https://files.pythonhosted.org/packages/aa/79/2626903efa84e9f5b9c8ee6972de8338673fdb5bb8d8d2797740bf911027/coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f", size = 210209 }, + { url = "https://files.pythonhosted.org/packages/cc/56/e1d75e8981a2a92c2a777e67c26efa96c66da59d645423146eb9ff3a851b/coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", size = 198954 }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "43.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/05/07b55d1fa21ac18c3a8c79f764e2514e6f6a9698f1be44994f5adf0d29db/cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", size = 686989 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/f3/01fdf26701a26f4b4dbc337a26883ad5bccaa6f1bbbdd29cd89e22f18a1c/cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", size = 6225303 }, + { url = "https://files.pythonhosted.org/packages/a3/01/4896f3d1b392025d4fcbecf40fdea92d3df8662123f6835d0af828d148fd/cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", size = 3760905 }, + { url = "https://files.pythonhosted.org/packages/0a/be/f9a1f673f0ed4b7f6c643164e513dbad28dd4f2dcdf5715004f172ef24b6/cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", size = 3977271 }, + { url = "https://files.pythonhosted.org/packages/4e/49/80c3a7b5514d1b416d7350830e8c422a4d667b6d9b16a9392ebfd4a5388a/cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", size = 3746606 }, + { url = "https://files.pythonhosted.org/packages/0e/16/a28ddf78ac6e7e3f25ebcef69ab15c2c6be5ff9743dd0709a69a4f968472/cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", size = 3986484 }, + { url = "https://files.pythonhosted.org/packages/01/f5/69ae8da70c19864a32b0315049866c4d411cce423ec169993d0434218762/cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", size = 3852131 }, + { url = "https://files.pythonhosted.org/packages/fd/db/e74911d95c040f9afd3612b1f732e52b3e517cb80de8bf183be0b7d413c6/cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", size = 4075647 }, + { url = "https://files.pythonhosted.org/packages/56/48/7b6b190f1462818b324e674fa20d1d5ef3e24f2328675b9b16189cbf0b3c/cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", size = 2623873 }, + { url = "https://files.pythonhosted.org/packages/eb/b1/0ebff61a004f7f89e7b65ca95f2f2375679d43d0290672f7713ee3162aff/cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", size = 3068039 }, + { url = "https://files.pythonhosted.org/packages/30/d5/c8b32c047e2e81dd172138f772e81d852c51f0f2ad2ae8a24f1122e9e9a7/cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", size = 6222984 }, + { url = "https://files.pythonhosted.org/packages/2f/78/55356eb9075d0be6e81b59f45c7b48df87f76a20e73893872170471f3ee8/cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", size = 3762968 }, + { url = "https://files.pythonhosted.org/packages/2a/2c/488776a3dc843f95f86d2f957ca0fc3407d0242b50bede7fad1e339be03f/cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", size = 3977754 }, + { url = "https://files.pythonhosted.org/packages/7c/04/2345ca92f7a22f601a9c62961741ef7dd0127c39f7310dffa0041c80f16f/cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", size = 3749458 }, + { url = "https://files.pythonhosted.org/packages/ac/25/e715fa0bc24ac2114ed69da33adf451a38abb6f3f24ec207908112e9ba53/cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", size = 3988220 }, + { url = "https://files.pythonhosted.org/packages/21/ce/b9c9ff56c7164d8e2edfb6c9305045fbc0df4508ccfdb13ee66eb8c95b0e/cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", size = 3853898 }, + { url = "https://files.pythonhosted.org/packages/2a/33/b3682992ab2e9476b9c81fff22f02c8b0a1e6e1d49ee1750a67d85fd7ed2/cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", size = 4076592 }, + { url = "https://files.pythonhosted.org/packages/81/1e/ffcc41b3cebd64ca90b28fd58141c5f68c83d48563c88333ab660e002cd3/cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", size = 2623145 }, + { url = "https://files.pythonhosted.org/packages/87/5c/3dab83cc4aba1f4b0e733e3f0c3e7d4386440d660ba5b1e3ff995feb734d/cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", size = 3068026 }, + { url = "https://files.pythonhosted.org/packages/6f/db/d8b8a039483f25fc3b70c90bc8f3e1d4497a99358d610c5067bf3bd4f0af/cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", size = 3144545 }, + { url = "https://files.pythonhosted.org/packages/93/90/116edd5f8ec23b2dc879f7a42443e073cdad22950d3c8ee834e3b8124543/cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", size = 3679828 }, + { url = "https://files.pythonhosted.org/packages/d8/32/1e1d78b316aa22c0ba6493cc271c1c309969e5aa5c22c830a1d7ce3471e6/cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", size = 3908132 }, + { url = "https://files.pythonhosted.org/packages/91/bb/cd2c13be3332e7af3cdf16154147952d39075b9f61ea5e6b5241bf4bf436/cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", size = 2988811 }, + { url = "https://files.pythonhosted.org/packages/cc/fc/ff7c76afdc4f5933b5e99092528d4783d3d1b131960fc8b31eb38e076ca8/cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", size = 3146844 }, + { url = "https://files.pythonhosted.org/packages/d7/29/a233efb3e98b13d9175dcb3c3146988ec990896c8fa07e8467cce27d5a80/cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", size = 3681997 }, + { url = "https://files.pythonhosted.org/packages/c0/cf/c9eea7791b961f279fb6db86c3355cfad29a73141f46427af71852b23b95/cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", size = 3905208 }, + { url = "https://files.pythonhosted.org/packages/21/ea/6c38ca546d5b6dab3874c2b8fc6b1739baac29bacdea31a8c6c0513b3cfa/cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", size = 2989787 }, +] + +[[package]] +name = "csscompressor" +version = "0.9.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/2a/8c3ac3d8bc94e6de8d7ae270bb5bc437b210bb9d6d9e46630c98f4abd20c/csscompressor-0.9.5.tar.gz", hash = "sha256:afa22badbcf3120a4f392e4d22f9fff485c044a1feda4a950ecc5eba9dd31a05", size = 237808 } + +[[package]] +name = "deprecated" +version = "1.2.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/14/1e41f504a246fc224d2ac264c227975427a85caf37c3979979edb9b1b232/Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3", size = 2974416 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/8d/778b7d51b981a96554f29136cd59ca7880bf58094338085bcf2a979a0e6a/Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", size = 9561 }, +] + +[[package]] +name = "distlib" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "filelock" +version = "3.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, +] + +[[package]] +name = "github-changelog-md" +version = "0.9.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygithub" }, + { name = "rich" }, + { name = "rtoml" }, + { name = "simple-toml-settings" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/27/612be87e501a7acc89d6c2a96ec598392e9f07ea9c94dfe4693ce587a8ad/github_changelog_md-0.9.5.tar.gz", hash = "sha256:d9aaa00bbcbd1fb3aa5a30a8227ce156c7df9a0c77d92ae085204833d7c0333d", size = 19538 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/c5/c8ad1efa195bf76db3f0fb2ac4c1a4f214bf46274762af4bd2df05df406d/github_changelog_md-0.9.5-py3-none-any.whl", hash = "sha256:eeae8f954a37a7fba00b922e56be7e3f89a91fc8156ce99dbd10be20e4ec1ee5", size = 18659 }, +] + +[[package]] +name = "htmlmin2" +version = "0.1.13" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/31/a76f4bfa885f93b8167cb4c85cf32b54d1f64384d0b897d45bc6d19b7b45/htmlmin2-0.1.13-py3-none-any.whl", hash = "sha256:75609f2a42e64f7ce57dbff28a39890363bde9e7e5885db633317efbdf8c79a2", size = 34486 }, +] + +[[package]] +name = "identify" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/79/7a520fc5011e02ca3f3285b5f6820eaf80443eb73e3733f73c02fb42ba0b/identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd", size = 99113 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/86/c4395700f3c5475424fb5c41e20c16be28d10c904aee4d005ba3217fc8e7/identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3", size = 98982 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, +] + +[[package]] +name = "jsmin" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/73/e01e4c5e11ad0494f4407a3f623ad4d87714909f50b17a06ed121034ff6e/jsmin-3.0.1.tar.gz", hash = "sha256:c0959a121ef94542e807a674142606f7e90214a2b3d1eb17300244bbb5cc2bfc", size = 13925 } + +[[package]] +name = "lice" +version = "0.13.0" +source = { editable = "." } +dependencies = [ + { name = "pyperclip" }, + { name = "rich" }, + { name = "simple-toml-settings" }, + { name = "single-source" }, + { name = "typer" }, +] + +[package.dev-dependencies] +dev = [ + { name = "github-changelog-md" }, + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocs-material" }, + { name = "mkdocs-minify-plugin" }, + { name = "mock" }, + { name = "mypy" }, + { name = "poethepoet" }, + { name = "pre-commit" }, + { name = "pyfakefs" }, + { name = "pygments" }, + { name = "pymarkdownlnt" }, + { name = "pymdown-extensions" }, + { name = "pytest" }, + { name = "pytest-clarity" }, + { name = "pytest-cov" }, + { name = "pytest-mock" }, + { name = "pytest-randomly" }, + { name = "pytest-reverse" }, + { name = "pytest-sugar" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "pyperclip", specifier = ">=1.9.0" }, + { name = "rich", specifier = ">=13.8.0" }, + { name = "simple-toml-settings", specifier = ">=0.8.0" }, + { name = "single-source", specifier = ">=0.4.0" }, + { name = "typer", specifier = ">=0.12.5" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "github-changelog-md", specifier = ">=0.9.5" }, + { name = "mkdocs", specifier = ">=1.6.1" }, + { name = "mkdocs-autorefs", specifier = ">=1.2.0" }, + { name = "mkdocs-material", specifier = ">=9.5.34" }, + { name = "mkdocs-minify-plugin", specifier = ">=0.8.0" }, + { name = "mock", specifier = ">=5.1.0" }, + { name = "mypy", specifier = ">=1.11.2" }, + { name = "poethepoet", specifier = ">=0.28.0" }, + { name = "pre-commit", specifier = ">=3.8.0" }, + { name = "pyfakefs", specifier = ">=5.6.0" }, + { name = "pygments", specifier = ">=2.18.0" }, + { name = "pymarkdownlnt", specifier = ">=0.9.22" }, + { name = "pymdown-extensions", specifier = ">=10.9" }, + { name = "pytest", specifier = ">=8.3.2" }, + { name = "pytest-clarity", specifier = ">=1.0.1" }, + { name = "pytest-cov", specifier = ">=5.0.0" }, + { name = "pytest-mock", specifier = ">=3.14.0" }, + { name = "pytest-randomly", specifier = ">=3.15.0" }, + { name = "pytest-reverse", specifier = ">=1.7.0" }, + { name = "pytest-sugar", specifier = ">=1.0.0" }, + { name = "ruff", specifier = ">=0.7.3" }, +] + +[[package]] +name = "markdown" +version = "3.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, + { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344 }, + { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389 }, + { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607 }, + { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728 }, + { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826 }, + { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843 }, + { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219 }, + { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946 }, + { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063 }, + { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, +] + +[[package]] +name = "mkdocs-autorefs" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/ae/0f1154c614d6a8b8a36fff084e5b82af3a15f7d2060cf0dcdb1c53297a71/mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f", size = 40262 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f", size = 16522 }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 }, +] + +[[package]] +name = "mkdocs-material" +version = "9.5.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/56/182d8121db9ab553cdf9bc58d5972b89833f60b63272f693c1f2b849b640/mkdocs_material-9.5.44.tar.gz", hash = "sha256:f3a6c968e524166b3f3ed1fb97d3ed3e0091183b0545cedf7156a2a6804c56c0", size = 3964306 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/eb/a801d00e0e210d82184aacce596906ec065422c78a7319244ba0771c4ded/mkdocs_material-9.5.44-py3-none-any.whl", hash = "sha256:47015f9c167d58a5ff5e682da37441fc4d66a1c79334bfc08d774763cacf69ca", size = 8674509 }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, +] + +[[package]] +name = "mkdocs-minify-plugin" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "csscompressor" }, + { name = "htmlmin2" }, + { name = "jsmin" }, + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/67/fe4b77e7a8ae7628392e28b14122588beaf6078b53eb91c7ed000fd158ac/mkdocs-minify-plugin-0.8.0.tar.gz", hash = "sha256:bc11b78b8120d79e817308e2b11539d790d21445eb63df831e393f76e52e753d", size = 8366 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/cd/2e8d0d92421916e2ea4ff97f10a544a9bd5588eb747556701c983581df13/mkdocs_minify_plugin-0.8.0-py3-none-any.whl", hash = "sha256:5fba1a3f7bd9a2142c9954a6559a57e946587b21f133165ece30ea145c66aee6", size = 6723 }, +] + +[[package]] +name = "mock" +version = "5.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/ab/41d09a46985ead5839d8be987acda54b5bb93f713b3969cc0be4f81c455b/mock-5.1.0.tar.gz", hash = "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d", size = 80232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/20/471f41173930550f279ccb65596a5ac19b9ac974a8d93679bcd3e0c31498/mock-5.1.0-py3-none-any.whl", hash = "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744", size = 30938 }, +] + +[[package]] +name = "mypy" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/8c/206de95a27722b5b5a8c85ba3100467bd86299d92a4f71c6b9aa448bfa2f/mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", size = 11020731 }, + { url = "https://files.pythonhosted.org/packages/ab/bb/b31695a29eea76b1569fd28b4ab141a1adc9842edde080d1e8e1776862c7/mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", size = 10184276 }, + { url = "https://files.pythonhosted.org/packages/a5/2d/4a23849729bb27934a0e079c9c1aad912167d875c7b070382a408d459651/mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", size = 12587706 }, + { url = "https://files.pythonhosted.org/packages/5c/c3/d318e38ada50255e22e23353a469c791379825240e71b0ad03e76ca07ae6/mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", size = 13105586 }, + { url = "https://files.pythonhosted.org/packages/4a/25/3918bc64952370c3dbdbd8c82c363804678127815febd2925b7273d9482c/mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", size = 9632318 }, + { url = "https://files.pythonhosted.org/packages/d0/19/de0822609e5b93d02579075248c7aa6ceaddcea92f00bf4ea8e4c22e3598/mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", size = 10939027 }, + { url = "https://files.pythonhosted.org/packages/c8/71/6950fcc6ca84179137e4cbf7cf41e6b68b4a339a1f5d3e954f8c34e02d66/mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", size = 10108699 }, + { url = "https://files.pythonhosted.org/packages/26/50/29d3e7dd166e74dc13d46050b23f7d6d7533acf48f5217663a3719db024e/mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", size = 12506263 }, + { url = "https://files.pythonhosted.org/packages/3f/1d/676e76f07f7d5ddcd4227af3938a9c9640f293b7d8a44dd4ff41d4db25c1/mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", size = 12984688 }, + { url = "https://files.pythonhosted.org/packages/9c/03/5a85a30ae5407b1d28fab51bd3e2103e52ad0918d1e68f02a7778669a307/mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", size = 9626811 }, + { url = "https://files.pythonhosted.org/packages/fb/31/c526a7bd2e5c710ae47717c7a5f53f616db6d9097caf48ad650581e81748/mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", size = 11077900 }, + { url = "https://files.pythonhosted.org/packages/83/67/b7419c6b503679d10bd26fc67529bc6a1f7a5f220bbb9f292dc10d33352f/mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", size = 10074818 }, + { url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 }, + { url = "https://files.pythonhosted.org/packages/1f/17/b1018c6bb3e9f1ce3956722b3bf91bff86c1cefccca71cec05eae49d6d41/mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", size = 13037783 }, + { url = "https://files.pythonhosted.org/packages/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 }, + { url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, + { url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, + { url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, + { url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, + { url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, + { url = "https://files.pythonhosted.org/packages/5f/d4/b33ddd40dad230efb317898a2d1c267c04edba73bc5086bf77edeb410fb2/mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc", size = 11013906 }, + { url = "https://files.pythonhosted.org/packages/f4/e6/f414bca465b44d01cd5f4a82761e15044bedd1bf8025c5af3cc64518fac5/mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732", size = 10180657 }, + { url = "https://files.pythonhosted.org/packages/38/e9/fc3865e417722f98d58409770be01afb961e2c1f99930659ff4ae7ca8b7e/mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc", size = 12586394 }, + { url = "https://files.pythonhosted.org/packages/2e/35/f4d8b6d2cb0b3dad63e96caf159419dda023f45a358c6c9ac582ccaee354/mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d", size = 13103591 }, + { url = "https://files.pythonhosted.org/packages/22/1d/80594aef135f921dd52e142fa0acd19df197690bd0cde42cea7b88cf5aa2/mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24", size = 9634690 }, + { url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 }, +] + +[[package]] +name = "pastel" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/f1/4594f5e0fcddb6953e5b8fe00da8c317b8b41b547e2b3ae2da7512943c62/pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d", size = 7555 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955 }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "poethepoet" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pastel" }, + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/07/dfaed168414cf1e10f5c90860cdc29ffd871df80be81f3d7abd0451a4508/poethepoet-0.30.0.tar.gz", hash = "sha256:9f7ccda2d6525616ce989ca8ef973739fd668f50bef0b9d3631421d504d9ae4a", size = 60139 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/98/12bff83ac39ba78ba4736c2f217bab294187de5d71ffbfeb3e126c230704/poethepoet-0.30.0-py3-none-any.whl", hash = "sha256:bf875741407a98da9e96f2f2d0b2c4c34f56d89939a7f53a4b6b3a64b546ec4e", size = 78036 }, +] + +[[package]] +name = "pprintpp" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/1a/7737e7a0774da3c3824d654993cf57adc915cb04660212f03406334d8c0b/pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403", size = 17995 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/d1/e4ed95fdd3ef13b78630280d9e9e240aeb65cc7c544ec57106149c3942fb/pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d", size = 16952 }, +] + +[[package]] +name = "pre-commit" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/c8/e22c292035f1bac8b9f5237a2622305bc0304e776080b246f3df57c4ff9f/pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2", size = 191678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/8f/496e10d51edd6671ebe0432e33ff800aa86775d2d147ce7d43389324a525/pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878", size = 218713 }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + +[[package]] +name = "pyfakefs" +version = "5.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/43/e0d0f256734cc03bcfb1f7d15d6bf19339531e8cd6fadfc3fcb8ae077a10/pyfakefs-5.7.1.tar.gz", hash = "sha256:24774c632f3b67ea26fd56b08115ba7c339d5cd65655410bca8572d73a1ae9a4", size = 211163 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/8b/0d90a59d571aa6df4f8ea18ced442a66203e20a4d97a71ba10a39595477c/pyfakefs-5.7.1-py3-none-any.whl", hash = "sha256:6503ffe7f401701cf974b502311f926da2b0657a72244a6ba36e985ceb3dd783", size = 226093 }, +] + +[[package]] +name = "pygithub" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "pynacl" }, + { name = "requests" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/ce/aa91d30040d9552c274e7ea8bd10a977600d508d579a4bb262b95eccf961/pygithub-2.5.0.tar.gz", hash = "sha256:e1613ac508a9be710920d26eb18b1905ebd9926aa49398e88151c1b526aad3cf", size = 3552804 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/05/bfbdbbc5d8aafd8dae9b3b6877edca561fccd8528ef5edc4e7b6d23721b5/PyGithub-2.5.0-py3-none-any.whl", hash = "sha256:b0b635999a658ab8e08720bdd3318893ff20e2275f6446fcf35bf3f44f2c0fd2", size = 375935 }, +] + +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + +[[package]] +name = "pyjwt" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344 }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pymarkdownlnt" +version = "0.9.25" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "application-properties" }, + { name = "columnar" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/e2/7f046674a1863a4481cd4fdfbb86813f474f7b96ad383ee356231ec6e871/pymarkdownlnt-0.9.25.tar.gz", hash = "sha256:f2c54cd1ec842610be607240e26bf3bc34b8d51b3c25083e26c9e5903fa54f62", size = 383011 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/de/3a573c1f972fcaed5854dbda9bb951594cc9c1757a220a34eea1e1ead8be/pymarkdownlnt-0.9.25-py3-none-any.whl", hash = "sha256:9a6e66cd821dca2ca57c3bedbe837867f41c74bf6609f10a9d827029dc71553c", size = 474227 }, +] + +[[package]] +name = "pymdown-extensions" +version = "10.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/0b/32f05854cfd432e9286bb41a870e0d1a926b72df5f5cdb6dec962b2e369e/pymdown_extensions-10.12.tar.gz", hash = "sha256:b0ee1e0b2bef1071a47891ab17003bfe5bf824a398e13f49f8ed653b699369a7", size = 840790 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/32/95a164ddf533bd676cbbe878e36e89b4ade3efde8dd61d0148c90cbbe57e/pymdown_extensions-10.12-py3-none-any.whl", hash = "sha256:49f81412242d3527b8b4967b990df395c89563043bc51a3d2d7d500e52123b77", size = 263448 }, +] + +[[package]] +name = "pynacl" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/22/27582568be639dfe22ddb3902225f91f2f17ceff88ce80e4db396c8986da/PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", size = 3392854 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/75/0b8ede18506041c0bf23ac4d8e2971b4161cd6ce630b177d0a08eb0d8857/PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", size = 349920 }, + { url = "https://files.pythonhosted.org/packages/59/bb/fddf10acd09637327a97ef89d2a9d621328850a72f1fdc8c08bdf72e385f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", size = 601722 }, + { url = "https://files.pythonhosted.org/packages/5d/70/87a065c37cca41a75f2ce113a5a2c2aa7533be648b184ade58971b5f7ccc/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", size = 680087 }, + { url = "https://files.pythonhosted.org/packages/ee/87/f1bb6a595f14a327e8285b9eb54d41fef76c585a0edef0a45f6fc95de125/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", size = 856678 }, + { url = "https://files.pythonhosted.org/packages/66/28/ca86676b69bf9f90e710571b67450508484388bfce09acf8a46f0b8c785f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", size = 1133660 }, + { url = "https://files.pythonhosted.org/packages/3d/85/c262db650e86812585e2bc59e497a8f59948a005325a11bbbc9ecd3fe26b/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", size = 663824 }, + { url = "https://files.pythonhosted.org/packages/fd/1a/cc308a884bd299b651f1633acb978e8596c71c33ca85e9dc9fa33a5399b9/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", size = 1117912 }, + { url = "https://files.pythonhosted.org/packages/25/2d/b7df6ddb0c2a33afdb358f8af6ea3b8c4d1196ca45497dd37a56f0c122be/PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543", size = 204624 }, + { url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", size = 212141 }, +] + +[[package]] +name = "pyperclip" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961 } + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "pytest-clarity" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pprintpp" }, + { name = "pytest" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/5c/cafa97944de55738a6a2c5a7cee00d073cb80495032d2b112c4546525eca/pytest-clarity-1.0.1.tar.gz", hash = "sha256:505fe345fad4fe11c6a4187fe683f2c7c52c077caa1e135f3e483fe112db7772", size = 4891 } + +[[package]] +name = "pytest-cov" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, +] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, +] + +[[package]] +name = "pytest-randomly" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396 }, +] + +[[package]] +name = "pytest-reverse" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5d/f1177a7ee9d104ec9f3067fc15ee21ce4b71b6d23ba827a08f4c51c39b30/pytest_reverse-1.8.0.tar.gz", hash = "sha256:eb72ffd57cc91061e837b1d2c4522bfda58eaa83fc97147fd90f7929160e97ab", size = 4438 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/e2/efa8261ea453103b8360d1313ca6578e3cdff2c76c9edd1ea99827175426/pytest_reverse-1.8.0-py3-none-any.whl", hash = "sha256:e31a2d0b51f2f8b6162aed268f853851f55c62ac445041a032740e985b0bc8c8", size = 4119 }, +] + +[[package]] +name = "pytest-sugar" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pytest" }, + { name = "termcolor" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/ac/5754f5edd6d508bc6493bc37d74b928f102a5fff82d9a80347e180998f08/pytest-sugar-1.0.0.tar.gz", hash = "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a", size = 14992 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/fb/889f1b69da2f13691de09a111c16c4766a433382d44aa0ecf221deded44a/pytest_sugar-1.0.0-py3-none-any.whl", hash = "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd", size = 10171 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, + { url = "https://files.pythonhosted.org/packages/58/58/7e4d9493a66c88a7da6d205768119f51af0f684fe7be7bac8328e217a52c/regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", size = 482669 }, + { url = "https://files.pythonhosted.org/packages/34/4c/8f8e631fcdc2ff978609eaeef1d6994bf2f028b59d9ac67640ed051f1218/regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/c5/1b/f0e4d13e6adf866ce9b069e191f303a30ab1277e037037a365c3aad5cc9c/regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/25/4d/ab21047f446693887f25510887e6820b93f791992994f6498b0318904d4a/regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", size = 792121 }, + { url = "https://files.pythonhosted.org/packages/45/ee/c867e15cd894985cb32b731d89576c41a4642a57850c162490ea34b78c3b/regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", size = 831275 }, + { url = "https://files.pythonhosted.org/packages/b3/12/b0f480726cf1c60f6536fa5e1c95275a77624f3ac8fdccf79e6727499e28/regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", size = 818257 }, + { url = "https://files.pythonhosted.org/packages/bf/ce/0d0e61429f603bac433910d99ef1a02ce45a8967ffbe3cbee48599e62d88/regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", size = 792727 }, + { url = "https://files.pythonhosted.org/packages/e4/c1/243c83c53d4a419c1556f43777ccb552bccdf79d08fda3980e4e77dd9137/regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", size = 780667 }, + { url = "https://files.pythonhosted.org/packages/c5/f4/75eb0dd4ce4b37f04928987f1d22547ddaf6c4bae697623c1b05da67a8aa/regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", size = 776963 }, + { url = "https://files.pythonhosted.org/packages/16/5d/95c568574e630e141a69ff8a254c2f188b4398e813c40d49228c9bbd9875/regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", size = 784700 }, + { url = "https://files.pythonhosted.org/packages/8e/b5/f8495c7917f15cc6fee1e7f395e324ec3e00ab3c665a7dc9d27562fd5290/regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", size = 848592 }, + { url = "https://files.pythonhosted.org/packages/1c/80/6dd7118e8cb212c3c60b191b932dc57db93fb2e36fb9e0e92f72a5909af9/regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", size = 852929 }, + { url = "https://files.pythonhosted.org/packages/11/9b/5a05d2040297d2d254baf95eeeb6df83554e5e1df03bc1a6687fc4ba1f66/regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", size = 781213 }, + { url = "https://files.pythonhosted.org/packages/26/b7/b14e2440156ab39e0177506c08c18accaf2b8932e39fb092074de733d868/regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", size = 261734 }, + { url = "https://files.pythonhosted.org/packages/80/32/763a6cc01d21fb3819227a1cc3f60fd251c13c37c27a73b8ff4315433a8e/regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", size = 274052 }, + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, + { url = "https://files.pythonhosted.org/packages/89/23/c4a86df398e57e26f93b13ae63acce58771e04bdde86092502496fa57f9c/regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839", size = 482682 }, + { url = "https://files.pythonhosted.org/packages/3c/8b/45c24ab7a51a1658441b961b86209c43e6bb9d39caf1e63f46ce6ea03bc7/regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e", size = 287679 }, + { url = "https://files.pythonhosted.org/packages/7a/d1/598de10b17fdafc452d11f7dada11c3be4e379a8671393e4e3da3c4070df/regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf", size = 284578 }, + { url = "https://files.pythonhosted.org/packages/49/70/c7eaa219efa67a215846766fde18d92d54cb590b6a04ffe43cef30057622/regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b", size = 782012 }, + { url = "https://files.pythonhosted.org/packages/89/e5/ef52c7eb117dd20ff1697968219971d052138965a4d3d9b95e92e549f505/regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0", size = 820580 }, + { url = "https://files.pythonhosted.org/packages/5f/3f/9f5da81aff1d4167ac52711acf789df13e789fe6ac9545552e49138e3282/regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b", size = 809110 }, + { url = "https://files.pythonhosted.org/packages/86/44/2101cc0890c3621b90365c9ee8d7291a597c0722ad66eccd6ffa7f1bcc09/regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef", size = 780919 }, + { url = "https://files.pythonhosted.org/packages/ce/2e/3e0668d8d1c7c3c0d397bf54d92fc182575b3a26939aed5000d3cc78760f/regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48", size = 771515 }, + { url = "https://files.pythonhosted.org/packages/a6/49/1bc4584254355e3dba930a3a2fd7ad26ccba3ebbab7d9100db0aff2eedb0/regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13", size = 696957 }, + { url = "https://files.pythonhosted.org/packages/c8/dd/42879c1fc8a37a887cd08e358af3d3ba9e23038cd77c7fe044a86d9450ba/regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2", size = 768088 }, + { url = "https://files.pythonhosted.org/packages/89/96/c05a0fe173cd2acd29d5e13c1adad8b706bcaa71b169e1ee57dcf2e74584/regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95", size = 774752 }, + { url = "https://files.pythonhosted.org/packages/b5/f3/a757748066255f97f14506483436c5f6aded7af9e37bca04ec30c90ca683/regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9", size = 838862 }, + { url = "https://files.pythonhosted.org/packages/5c/93/c6d2092fd479dcaeea40fc8fa673822829181ded77d294a7f950f1dda6e2/regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f", size = 842622 }, + { url = "https://files.pythonhosted.org/packages/ff/9c/daa99532c72f25051a90ef90e1413a8d54413a9e64614d9095b0c1c154d0/regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b", size = 772713 }, + { url = "https://files.pythonhosted.org/packages/13/5d/61a533ccb8c231b474ac8e3a7d70155b00dfc61af6cafdccd1947df6d735/regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57", size = 261756 }, + { url = "https://files.pythonhosted.org/packages/dc/7b/e59b7f7c91ae110d154370c24133f947262525b5d6406df65f23422acc17/regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983", size = 274110 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rich" +version = "13.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, +] + +[[package]] +name = "rtoml" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/93/59e1dc9829eafbfb349b1ff2dcfca647d7f7e7d87788de54ab0e402c7036/rtoml-0.12.0.tar.gz", hash = "sha256:662e56bd5953ee7ebcc5798507ae90daa329940a5d5157a48f3d477ebf99c55b", size = 43127 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/de/08dc63ef974b6720e1f6159a4d3b36f0cb40d2d1c4a6315ebdf0bbf78ef7/rtoml-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:750761d30c70ffd45cd30ef8982e4c0665e76914efcc828ff4cd8450acddd328", size = 324818 }, + { url = "https://files.pythonhosted.org/packages/b1/91/3c1454fdc0562318b3ef33dc60d365ba4fb8b5b8d252802b3f4a4046fa4b/rtoml-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:af6dd6adc39a5be17dc6b07e13c1dd0e07af095a909e04355b756ad7ee7a7211", size = 313497 }, + { url = "https://files.pythonhosted.org/packages/95/7e/6554227a80e750a6b27b0439f94d5406b0db479bb07c7b29437dbc4fbab0/rtoml-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f4f3f7667c4d030669ae378da5d15a5c8dcb0065d12d2505b676f84828426b0", size = 341331 }, + { url = "https://files.pythonhosted.org/packages/7f/f9/567a5353b3c30fa484e851d5cd0fc689efc48aa3edd6e28ce765e0c6f874/rtoml-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76261f8ffdf78f0947c6628f364807073f3d30c2f480f5d7ee40d09e951ec84a", size = 360949 }, + { url = "https://files.pythonhosted.org/packages/7c/d1/5d914a94b49b3695d1ab125fb00cb277bff5027dd0ef29b3b903ccbe9f42/rtoml-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71884d293c34abf37d14b5e561ea0e57d71caa81b6f42c4c04120c7dd19650ca", size = 384408 }, + { url = "https://files.pythonhosted.org/packages/7e/89/74f435cc713bf9c8f7cef11fa24999f85dfb9f8ff84ce79bff0c905fa6a2/rtoml-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d991801446b964040b914527c62ae42d3f36be52a45be1d1f5fc2f36aa1dce3", size = 485025 }, + { url = "https://files.pythonhosted.org/packages/d0/70/085af811ed39fb39ed7063a052474c0deb34e28df4ab5bb3c3a6d0e04e94/rtoml-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08da11609dab48b57ee2969beec593863db1f83957d0879a8bb88d2d41b44f2c", size = 349186 }, + { url = "https://files.pythonhosted.org/packages/71/f9/d31a3198bc8f1e690e4273d15195e8a6319fd3f1618f7bd7121af1ffd25a/rtoml-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a2dbb5aa11ab76e4f2f6fcfc53996eb1a3aaedd8465352b597a8a70e1ec0818", size = 368224 }, + { url = "https://files.pythonhosted.org/packages/49/62/f201d4b58df8b97512e922c4a9d8a62f51febca1e9ca0d1d8a3b789a3f42/rtoml-0.12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ded14b9b0fce50bfe38eab6a3f8300eb969019f69bd64a3f6eb1b47949d9f34d", size = 520403 }, + { url = "https://files.pythonhosted.org/packages/5e/1c/20a9aa9ccaaadd82bde1388c8e579528e68810e426bde79ca35c3341aeb6/rtoml-0.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:79adf4665f50153cb1b625bb1271fd9c0362ce48ffb7ee12c729e7f8087242ce", size = 520124 }, + { url = "https://files.pythonhosted.org/packages/ac/b6/5d136a24a9252edae5ce4613fe531a379f73dbbf1fbcbad869503b831f74/rtoml-0.12.0-cp310-cp310-win32.whl", hash = "sha256:17b9628a7c70404fdd440d95eea5ba749653f000773df868d4accc2d61760db4", size = 219055 }, + { url = "https://files.pythonhosted.org/packages/53/be/c0a13f0b2b1317785592806e143819af8dc2cf35e6ecd62e260274f730a7/rtoml-0.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:540e461998f419a11fd73ebd2aa6de8986af8348ddfd18d2eb2c5f57ec9ed08d", size = 224617 }, + { url = "https://files.pythonhosted.org/packages/56/16/a6612dd636be6ff56ed285bfffa938915fa62fdacad8d8c6b13586374ad5/rtoml-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d986a7ea113122023a76ff9b2ed40ecc86ff9ed1e5c459010b6b06b5f05ef4ed", size = 325059 }, + { url = "https://files.pythonhosted.org/packages/7f/f0/cef59ce5f4a72a92562c07c94c4d15f6c03b92bb3e385eb4cdd4136bca6b/rtoml-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0229a51ec690b30a899b60ec06ae132c4ebf86bc81efd2a9a131f482570324d1", size = 313693 }, + { url = "https://files.pythonhosted.org/packages/81/cd/6a45e07aba35f0c40d6628a237f6b61d940b2fe60799ad16364e50bcac6f/rtoml-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51c9112935bd33dd9d30d45ff37567f0ece78b0ff5aa823072d448a96693f429", size = 341305 }, + { url = "https://files.pythonhosted.org/packages/8c/5d/0ffb6243e009472d2ce58b794830105067b03f01648a6c8c76bce9bc8fbb/rtoml-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:69a0bbd81ab27272845f2d2c211f7a1fc18d16ef6fc756796ec636589867c1e5", size = 361097 }, + { url = "https://files.pythonhosted.org/packages/59/c5/182d70e7f3ec00afaffaf979fe1ddcccfe9afaa00ccda38c09be5375cbeb/rtoml-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90becb592ac6129b132d299fc4c911c470fbf88d032a0df7987f9a30c8260966", size = 384548 }, + { url = "https://files.pythonhosted.org/packages/1a/5f/1797307c95db6b934cb9724fefe09200ed4363d670984da9505c3e00c723/rtoml-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d70ac00b0d838f5e54a5d957a74399aac2e671c60354f6457e0400c5e509d83d", size = 484113 }, + { url = "https://files.pythonhosted.org/packages/f1/4e/fbd9c680da5f6f0788164109a326c5727c2827828fa202203e36418d1f7f/rtoml-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53ce9204b52a51cb4d7aa29eb846cd78ce8644f3750c8de07f07f1561150c109", size = 349152 }, + { url = "https://files.pythonhosted.org/packages/8e/d4/523f17e7819dda78e29362b8ece4a6dd398099b40b8faaf238633aad5fbd/rtoml-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1b59008b2e8e5216aab65a9a711df032a89ef91c5bd66a1e22c74cd5ea4dfe7a", size = 368377 }, + { url = "https://files.pythonhosted.org/packages/e9/3e/219c8222dc226eb6b42b9f7e8cf9af0f05479cb9328c1f74645da106bd11/rtoml-0.12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a571e582b14cf4d36f52ae2066c098e4265714780db9d2ba1f1f2fc6718cf7e", size = 520247 }, + { url = "https://files.pythonhosted.org/packages/c9/cf/c1dea06ad0ecc59950cf773bb99a265af86210fd6b6dc4c66984ec9b32bd/rtoml-0.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4171fce22163ba0c5f9ca07320d768e25fd3c5603cf56366f327443e60aabc8c", size = 520088 }, + { url = "https://files.pythonhosted.org/packages/ab/80/a42d1bada534817ce91633db8696fa58b9c6d3cde0a8142a944c0cb96ecb/rtoml-0.12.0-cp311-cp311-win32.whl", hash = "sha256:1f11b74bd8f730bb87fdbace4367d49adec006b75228fea869da3e9e460a20b2", size = 219441 }, + { url = "https://files.pythonhosted.org/packages/0f/e9/00ab4b4da40e254d36baf670b67da88240990a86d44fc78b9d6a642563d7/rtoml-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:6bc52a5d177668d9244c09aad75df8dc9a022155e4002850c03badba51585e5c", size = 224793 }, + { url = "https://files.pythonhosted.org/packages/4c/ec/993038e802e5eded28e3ed680c31755e833ba82bb8bbc52eb9f1c3ea2504/rtoml-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:e8308f6b585f5b9343fc54bd028d2662c0d6637fa123d5f8b96beef4626a323a", size = 216905 }, + { url = "https://files.pythonhosted.org/packages/fc/f8/ab3712301107d19ef256338838af335378cb87c43cc5144e159c9fb46222/rtoml-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac75a75f15924fa582df465a3b1f4495710e3d4e1930837423ea396bcb1549b6", size = 322966 }, + { url = "https://files.pythonhosted.org/packages/ba/cc/499c45159e96247167c6e3ee293f2d4f16f7e7d9c1585025bbb902de57a3/rtoml-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd895de2745b4874498608948a9496e587b3154903ca8c6b4dec8f8b6c2a5252", size = 311730 }, + { url = "https://files.pythonhosted.org/packages/73/2a/a97927be7b586c9f50825295b3ff34d30c06ebaa41593a961645e0c84c4d/rtoml-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c1c82d2a79a943c33b851ec3745580ea93fbc40dcb970288439107b6e4a7062", size = 338995 }, + { url = "https://files.pythonhosted.org/packages/8f/22/fc829b0282c20dde98a66625ebc67a2d3bd9c3bb185e19b8dc09fac6b2ee/rtoml-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5ada7cc9fc0b94d1f5095d71d8966d10ee2628d69c574e3ef8c9e6dd36a9d525", size = 359621 }, + { url = "https://files.pythonhosted.org/packages/4c/a4/96500a6d80c694813c0a795a90ec41d174344ce66acba8edb9507a3816d7/rtoml-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7e4c13ed587d5fc8012aaacca3b73d283191f5462f27b005cadbf9a30083428", size = 382684 }, + { url = "https://files.pythonhosted.org/packages/61/60/439bfff454a66c6cb197923400a9d07fd4664edf237983efcad8df1633a6/rtoml-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd24ed60f588aa7262528bfabf97ebf776ff1948ae78829c00389813cd482374", size = 482316 }, + { url = "https://files.pythonhosted.org/packages/d7/67/32b5f4ccb06876eec4bd339dc739e5e0ae30f3494f88012f9d293d265d9e/rtoml-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:827159e7313fa35b8495c3ec1c54526ccd2fbd9713084ad959c4455749b4a68d", size = 347280 }, + { url = "https://files.pythonhosted.org/packages/65/36/a0cab2a2a2e00c351d19706ea0afd4034529a9402b7577051baf4ec5cf34/rtoml-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fad4117620e22482468f28556362e778d44c2065dfac176bf42ac4997214ae4", size = 366405 }, + { url = "https://files.pythonhosted.org/packages/88/a8/155fa88275e54a3b336ab5c0dec2bad5c374d6a1c4bf085deffd16baf09a/rtoml-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5248359a67aa034e409f2b06fed02de964bf9dd7f401661076dd7ddf3a81659b", size = 518700 }, + { url = "https://files.pythonhosted.org/packages/04/80/5fe39d943ba2a40ef2dcf8af00fa0bf35d18b6d495abdacc5b67502a194b/rtoml-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:28a81c9335f2d7b9cdb6053940b35c590c675222d4935f7a4b8751071e5a5519", size = 518107 }, + { url = "https://files.pythonhosted.org/packages/fb/25/f5b371c08269db9a0c4df5e80244c7a2d21e41197f4d66ea80556fbaaa83/rtoml-0.12.0-cp312-cp312-win32.whl", hash = "sha256:b28c7882f60622645ff7dd180ddb85f4e018406b674ea86f65d99ac0f75747bc", size = 220464 }, + { url = "https://files.pythonhosted.org/packages/b8/d9/5e6df3255f3eb277a8b6b3c421aba85803d9aa73a9562c50878642b9b300/rtoml-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:d7e187c38a86202bde843a517d341c026f7b0eb098ad5396ed40f93170565bd7", size = 225520 }, + { url = "https://files.pythonhosted.org/packages/d5/b4/605d263956ef7287519df9c269de0409ea6589f4b1ddf6ce9e6d58a61e30/rtoml-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:477131a487140163cc9850a66d92a864fb507b37d81fb3366ad5203d30c85520", size = 217230 }, + { url = "https://files.pythonhosted.org/packages/88/f5/35c0dcfb152300980c05c8c810bd9927fa204db9722917a11d617718ce8c/rtoml-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12e99b493f0d59ad925b307b4c3b15c560ee44c672dce2ddce227e550560af5e", size = 322647 }, + { url = "https://files.pythonhosted.org/packages/3a/ed/d1d50706ff2ab0a934437609320fd8c4e0834e9bb5bba273ad76e86c9ca0/rtoml-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a058a1739a2519a41afe160280dcd791c202068e477ceb7ebf606830299c63af", size = 311468 }, + { url = "https://files.pythonhosted.org/packages/dc/0f/cb0c0b3db93775e3dfa7b83bfe8f9df7f75a2b61934c668cfaa377adcee2/rtoml-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f5ee3825c9c7aad732b184fed58cc2c368360ca8d553516663374937b9497be", size = 338539 }, + { url = "https://files.pythonhosted.org/packages/cd/49/3ce420d49d9beae463a08326dbe276dbb8f9c76730ffbc4e49349cc5ba32/rtoml-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3637da07651aa522fcaa81d7944167a9db886c687ec81c31aade0048caa51c97", size = 359135 }, + { url = "https://files.pythonhosted.org/packages/26/f5/257d2d2561597c3286e036053b6af4bd0f488d7adaf9e213ea1cf8c1e3a2/rtoml-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:559f77c916cf02e0261756a7924382e5b4a529a316106aba9b7ff4b3b39e227a", size = 382226 }, + { url = "https://files.pythonhosted.org/packages/f8/94/c415547d83b5831ef61302a41929d5013dc4d03c38fa77289f49c34e32e0/rtoml-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b9156c2d30a2917f172b9a98c251864d3063dc5bc9764147779245c8a690441", size = 482014 }, + { url = "https://files.pythonhosted.org/packages/e8/b8/87074f0c3f14b27dbe0eedd87cf41a5b00d4c12a06e16d76d6167f42a65d/rtoml-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bea9797f08311b0b605cae671abd884724d8d3d6524c184ccf8c70b220a9a68b", size = 346701 }, + { url = "https://files.pythonhosted.org/packages/27/57/2c25850a7f5597eaacc815194df3f78b99ff04201f48c2a573c0c1e05f97/rtoml-0.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b522f671f8964a79dda162c9985950422e27fe9420dd924257dee0184c8d047f", size = 365954 }, + { url = "https://files.pythonhosted.org/packages/fe/63/892c5c2087a159cd5bad8cab759b015fdd185d50ba97a91725548435b1f9/rtoml-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:321ee9dca365b5c1dab8c74617e7f8c941de3fdc10ac9f3c11c9ac261418ed80", size = 518241 }, + { url = "https://files.pythonhosted.org/packages/b6/96/89c80a946adbd2050999b7cee974120c28df16683b8a5bbf3a09ea6b2a1b/rtoml-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:57912b150aa48a8a90b599b57691a165092a9f5cf9a98bf431b1cd380e58414a", size = 517418 }, + { url = "https://files.pythonhosted.org/packages/3b/88/354f0f3388b38cb50a58e4abbeb364e08b7f6739f79c8a1a03f3326caaec/rtoml-0.12.0-cp313-cp313-win32.whl", hash = "sha256:7aebc94ed208ff46e6ce469ef30b98095932a3e74b99bde102a0f035d5034620", size = 220123 }, + { url = "https://files.pythonhosted.org/packages/aa/c8/8dc7e391ef6ee8967a8ac1a2c40e483d99b6c0e09c96ce0e5d4c01f88b9c/rtoml-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c88e48946adef48dce2dc54f1380f6ff0d580f06770f9ca9600ef330bc06c39", size = 225143 }, + { url = "https://files.pythonhosted.org/packages/a6/40/2e8640ffe564626424aba6f1ea19f41f278e46932e5431faf8265f6e1dcb/rtoml-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:730770673649220d4265d9986d3a9089d38434f36c1c629b98a58eb2bbee9cfb", size = 216902 }, + { url = "https://files.pythonhosted.org/packages/a1/bc/a87c6c4ffeb629460c7c4c4e03d824c5752a65f6a5b5e99cccbd7c103c61/rtoml-0.12.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:9d3d266cbb0d42cf83658eb0ecc40288036fe986b200cefd2c6ad8e3c714b4cf", size = 325737 }, + { url = "https://files.pythonhosted.org/packages/d2/fa/9f9ff455a2d49ef269b9c4bb54ace30e7262d8184257cd61ac83b65cb218/rtoml-0.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e919d19518a8f3c769601105677c2c2c73c1a7a1ac4306830f570801abf3299", size = 314776 }, + { url = "https://files.pythonhosted.org/packages/36/f3/201f189b5d868da51a306cddc6ce5f74560ff0e9238405c666de374c212c/rtoml-0.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6ca7405f3bb45307029156b2f69c7048cc8c0cd840356f81f64091030adeb", size = 342129 }, + { url = "https://files.pythonhosted.org/packages/35/06/7fd09e8f84d5768d61f7878fd3684547c51a0c711acfaf3e7de4f854c18e/rtoml-0.12.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c7865af375c8f40e75bcf82cbb10e20d662f239a9f49e5597e28742c938f4e5", size = 362070 }, + { url = "https://files.pythonhosted.org/packages/7d/1b/e3128ea66d5e5c77f1f3e4c42f0423833d3ef93ddfb433a342e332a727ee/rtoml-0.12.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:67e7c7c61224d2b31aa2d6f9bbdd81011a505cb0388f2e9e6d815a840dd6c39a", size = 385087 }, + { url = "https://files.pythonhosted.org/packages/77/37/8f5d06b58121764563061f59258d6b23aba46ce33e40df875f323ccc09b4/rtoml-0.12.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:206c7ba5ab2a4b5f452565b1751430cc14d7b1423045370e5968a0e5a15846a7", size = 486316 }, + { url = "https://files.pythonhosted.org/packages/bf/35/a95e2d373f48cb8b51e3b10ae3d266b04c206c1cae48f85f28acbdbff27a/rtoml-0.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8f4ae09e9ca8de5bd874b661302f8083dc1a47b0865f99f7becf24903f76736", size = 350093 }, + { url = "https://files.pythonhosted.org/packages/2f/7c/e3653dbe929e6723512beabc4229b443d5d0051a4c64b1e821693a3c67dc/rtoml-0.12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3c7b633b74f7590f4c1e1fe36c1e6a26ca6dfa6491b9d91530d6e907b29d296b", size = 369261 }, + { url = "https://files.pythonhosted.org/packages/3c/67/1f2fdd00697f89ec9045eefcf41f7d28b7a05f73e9593d3667c76a42857e/rtoml-0.12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:63f6742f3e0dd076309c195af422447513ccace978023784607ee22302f4a900", size = 521115 }, + { url = "https://files.pythonhosted.org/packages/67/6e/e9f3f60adf30c5f30554609f62bd31914fa3089e8c6c19f2ce8d83f91ee5/rtoml-0.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9d5564dcc5ca1755f5bae59e036fb4e255ed59a9f8af836eb2d9765d125b11bb", size = 521075 }, + { url = "https://files.pythonhosted.org/packages/c9/97/aaf3386a8b2c0763f1e1fa8e5a302611289399f8ecb2a67beef9ff09ae64/rtoml-0.12.0-cp39-cp39-win32.whl", hash = "sha256:fe180b78d026499ee3b42c368a6c060a3d5b23838f17dde42d099839a8f8a2c6", size = 220215 }, + { url = "https://files.pythonhosted.org/packages/29/ce/e4f54612e662c4803816ea65cee8eacd13f2919be068b65ef7f459d772d7/rtoml-0.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:b7c6bdc9128c0a4ebf45e6720ae03c99ed7443a7135e494d93d3c30c14769eb3", size = 225518 }, +] + +[[package]] +name = "ruff" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/06/09d1276df977eece383d0ed66052fc24ec4550a61f8fbc0a11200e690496/ruff-0.7.3.tar.gz", hash = "sha256:e1d1ba2e40b6e71a61b063354d04be669ab0d39c352461f3d789cac68b54a313", size = 3243664 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/56/933d433c2489e4642487b835f53dd9ff015fb3d8fa459b09bb2ce42d7c4b/ruff-0.7.3-py3-none-linux_armv6l.whl", hash = "sha256:34f2339dc22687ec7e7002792d1f50712bf84a13d5152e75712ac08be565d344", size = 10372090 }, + { url = "https://files.pythonhosted.org/packages/20/ea/1f0a22a6bcdd3fc26c73f63a025d05bd565901b729d56bcb093c722a6c4c/ruff-0.7.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fb397332a1879b9764a3455a0bb1087bda876c2db8aca3a3cbb67b3dbce8cda0", size = 10190037 }, + { url = "https://files.pythonhosted.org/packages/16/74/aca75666e0d481fe394e76a8647c44ea919087748024924baa1a17371e3e/ruff-0.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:37d0b619546103274e7f62643d14e1adcbccb242efda4e4bdb9544d7764782e9", size = 9811998 }, + { url = "https://files.pythonhosted.org/packages/20/a1/cf446a0d7f78ea1f0bd2b9171c11dfe746585c0c4a734b25966121eb4f5d/ruff-0.7.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59f0c3ee4d1a6787614e7135b72e21024875266101142a09a61439cb6e38a5", size = 10620626 }, + { url = "https://files.pythonhosted.org/packages/cd/c1/82b27d09286ae855f5d03b1ad37cf243f21eb0081732d4d7b0d658d439cb/ruff-0.7.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:44eb93c2499a169d49fafd07bc62ac89b1bc800b197e50ff4633aed212569299", size = 10177598 }, + { url = "https://files.pythonhosted.org/packages/b9/42/c0acac22753bf74013d035a5ef6c5c4c40ad4d6686bfb3fda7c6f37d9b37/ruff-0.7.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d0242ce53f3a576c35ee32d907475a8d569944c0407f91d207c8af5be5dae4e", size = 11171963 }, + { url = "https://files.pythonhosted.org/packages/43/18/bb0befb7fb9121dd9009e6a72eb98e24f1bacb07c6f3ecb55f032ba98aed/ruff-0.7.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6b6224af8b5e09772c2ecb8dc9f3f344c1aa48201c7f07e7315367f6dd90ac29", size = 11856157 }, + { url = "https://files.pythonhosted.org/packages/5e/91/04e98d7d6e32eca9d1372be595f9abc7b7f048795e32eb2edbd8794d50bd/ruff-0.7.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c50f95a82b94421c964fae4c27c0242890a20fe67d203d127e84fbb8013855f5", size = 11440331 }, + { url = "https://files.pythonhosted.org/packages/f5/dc/3fe99f2ce10b76d389041a1b9f99e7066332e479435d4bebcceea16caff5/ruff-0.7.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f3eff9961b5d2644bcf1616c606e93baa2d6b349e8aa8b035f654df252c8c67", size = 12725354 }, + { url = "https://files.pythonhosted.org/packages/43/7b/1daa712de1c5bc6cbbf9fa60e9c41cc48cda962dc6d2c4f2a224d2c3007e/ruff-0.7.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8963cab06d130c4df2fd52c84e9f10d297826d2e8169ae0c798b6221be1d1d2", size = 11010091 }, + { url = "https://files.pythonhosted.org/packages/b6/db/1227a903587432eb569e57a95b15a4f191a71fe315cde4c0312df7bc85da/ruff-0.7.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:61b46049d6edc0e4317fb14b33bd693245281a3007288b68a3f5b74a22a0746d", size = 10610687 }, + { url = "https://files.pythonhosted.org/packages/db/e2/dc41ee90c3085aadad4da614d310d834f641aaafddf3dfbba08210c616ce/ruff-0.7.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:10ebce7696afe4644e8c1a23b3cf8c0f2193a310c18387c06e583ae9ef284de2", size = 10254843 }, + { url = "https://files.pythonhosted.org/packages/6f/09/5f6cac1c91542bc5bd33d40b4c13b637bf64d7bb29e091dadb01b62527fe/ruff-0.7.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3f36d56326b3aef8eeee150b700e519880d1aab92f471eefdef656fd57492aa2", size = 10730962 }, + { url = "https://files.pythonhosted.org/packages/d3/42/89a4b9a24ef7d00269e24086c417a006f9a3ffeac2c80f2629eb5ce140ee/ruff-0.7.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5d024301109a0007b78d57ab0ba190087b43dce852e552734ebf0b0b85e4fb16", size = 11101907 }, + { url = "https://files.pythonhosted.org/packages/b0/5c/efdb4777686683a8edce94ffd812783bddcd3d2454d38c5ac193fef7c500/ruff-0.7.3-py3-none-win32.whl", hash = "sha256:4ba81a5f0c5478aa61674c5a2194de8b02652f17addf8dfc40c8937e6e7d79fc", size = 8611095 }, + { url = "https://files.pythonhosted.org/packages/bb/b8/28fbc6a4efa50178f973972d1c84b2d0a33cdc731588522ab751ac3da2f5/ruff-0.7.3-py3-none-win_amd64.whl", hash = "sha256:588a9ff2fecf01025ed065fe28809cd5a53b43505f48b69a1ac7707b1b7e4088", size = 9418283 }, + { url = "https://files.pythonhosted.org/packages/3f/77/b587cba6febd5e2003374f37eb89633f79f161e71084f94057c8653b7fb3/ruff-0.7.3-py3-none-win_arm64.whl", hash = "sha256:1713e2c5545863cdbfe2cbce21f69ffaf37b813bfd1fb3b90dc9a6f1963f5a8c", size = 8725228 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[package]] +name = "simple-toml-settings" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "rtoml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/8a/c7b8d61a635be56fec000a673e2696ba5fccbc893a6c5bafdc8516ae248c/simple_toml_settings-0.9.0.tar.gz", hash = "sha256:64070c7e011c007e41b73455c309a514c9ccec6a4581ee46a6af643e5dbf00d6", size = 105798 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/da/23380ea62d22c06b6ff2a485fa701c8bf0243e6f4a1ef70e82fcc3b8867a/simple_toml_settings-0.9.0-py3-none-any.whl", hash = "sha256:943ef93223085fde9016b3d8b3f2eb04e346df58449bc1c9de32358191559d20", size = 8364 }, +] + +[[package]] +name = "single-source" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/c5/096cda37599fb12f9930266ebb66e72e0ef3c39eb8be1934025f44e9c7ed/single_source-0.4.0.tar.gz", hash = "sha256:7917aa113bda60072f01952e2966cd7247f0ec16fe52a1555f3c066b553e98b4", size = 4803 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/34/c26bc3fbd88437ae3417f57f10c5eff926e095cc3d61a235478907c28b2d/single_source-0.4.0-py3-none-any.whl", hash = "sha256:38880b16e6e0ca2e012f85dc3820eb31999ace5f1d9a588395ea38f8bd0775f5", size = 5590 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "termcolor" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/72/88311445fd44c455c7d553e61f95412cf89054308a1aa2434ab835075fc5/termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f", size = 13057 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", size = 7755 }, +] + +[[package]] +name = "tomli" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/e4/1b6cbcc82d8832dd0ce34767d5c560df8a3547ad8cbc427f34601415930a/tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8", size = 16622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/f7/4da0ffe1892122c9ea096c57f64c2753ae5dd3ce85488802d11b0992cc6d/tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391", size = 13750 }, +] + +[[package]] +name = "toolz" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383 }, +] + +[[package]] +name = "typer" +version = "0.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/58/a79003b91ac2c6890fc5d90145c662fd5771c6f11447f116b63300436bc9/typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722", size = 98953 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/2b/886d13e742e514f704c33c4caa7df0f3b89e5a25ef8db02aa9ca3d9535d5/typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b", size = 47288 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] + +[[package]] +name = "virtualenv" +version = "20.27.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/b3/7b6a79c5c8cf6d90ea681310e169cf2db2884f4d583d16c6e1d5a75a4e04/virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba", size = 6491145 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/92/78324ff89391e00c8f4cf6b8526c41c6ef36b4ea2d2c132250b1a6fc2b8d/virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4", size = 3117838 }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390 }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389 }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020 }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393 }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392 }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019 }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471 }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449 }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054 }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, + { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390 }, + { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386 }, + { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017 }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902 }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380 }, + { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903 }, + { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "wrapt" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/4c/063a912e20bcef7124e0df97282a8af3ff3e4b603ce84c481d6d7346be0a/wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", size = 53972 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/c6/5375258add3777494671d8cec27cdf5402abd91016dee24aa2972c61fedf/wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4", size = 37315 }, + { url = "https://files.pythonhosted.org/packages/32/12/e11adfde33444986135d8881b401e4de6cbb4cced046edc6b464e6ad7547/wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", size = 38160 }, + { url = "https://files.pythonhosted.org/packages/70/7d/3dcc4a7e96f8d3e398450ec7703db384413f79bd6c0196e0e139055ce00f/wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", size = 80419 }, + { url = "https://files.pythonhosted.org/packages/d1/c4/8dfdc3c2f0b38be85c8d9fdf0011ebad2f54e40897f9549a356bebb63a97/wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", size = 72669 }, + { url = "https://files.pythonhosted.org/packages/49/83/b40bc1ad04a868b5b5bcec86349f06c1ee1ea7afe51dc3e46131e4f39308/wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", size = 80271 }, + { url = "https://files.pythonhosted.org/packages/19/d4/cd33d3a82df73a064c9b6401d14f346e1d2fb372885f0295516ec08ed2ee/wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", size = 84748 }, + { url = "https://files.pythonhosted.org/packages/ef/58/2fde309415b5fa98fd8f5f4a11886cbf276824c4c64d45a39da342fff6fe/wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", size = 77522 }, + { url = "https://files.pythonhosted.org/packages/07/44/359e4724a92369b88dbf09878a7cde7393cf3da885567ea898e5904049a3/wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", size = 84780 }, + { url = "https://files.pythonhosted.org/packages/88/8f/706f2fee019360cc1da652353330350c76aa5746b4e191082e45d6838faf/wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", size = 35335 }, + { url = "https://files.pythonhosted.org/packages/19/2b/548d23362e3002ebbfaefe649b833fa43f6ca37ac3e95472130c4b69e0b4/wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", size = 37528 }, + { url = "https://files.pythonhosted.org/packages/fd/03/c188ac517f402775b90d6f312955a5e53b866c964b32119f2ed76315697e/wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", size = 37313 }, + { url = "https://files.pythonhosted.org/packages/0f/16/ea627d7817394db04518f62934a5de59874b587b792300991b3c347ff5e0/wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", size = 38164 }, + { url = "https://files.pythonhosted.org/packages/7f/a7/f1212ba098f3de0fd244e2de0f8791ad2539c03bef6c05a9fcb03e45b089/wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", size = 80890 }, + { url = "https://files.pythonhosted.org/packages/b7/96/bb5e08b3d6db003c9ab219c487714c13a237ee7dcc572a555eaf1ce7dc82/wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", size = 73118 }, + { url = "https://files.pythonhosted.org/packages/6e/52/2da48b35193e39ac53cfb141467d9f259851522d0e8c87153f0ba4205fb1/wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", size = 80746 }, + { url = "https://files.pythonhosted.org/packages/11/fb/18ec40265ab81c0e82a934de04596b6ce972c27ba2592c8b53d5585e6bcd/wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", size = 85668 }, + { url = "https://files.pythonhosted.org/packages/0f/ef/0ecb1fa23145560431b970418dce575cfaec555ab08617d82eb92afc7ccf/wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", size = 78556 }, + { url = "https://files.pythonhosted.org/packages/25/62/cd284b2b747f175b5a96cbd8092b32e7369edab0644c45784871528eb852/wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", size = 85712 }, + { url = "https://files.pythonhosted.org/packages/e5/a7/47b7ff74fbadf81b696872d5ba504966591a3468f1bc86bca2f407baef68/wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", size = 35327 }, + { url = "https://files.pythonhosted.org/packages/cf/c3/0084351951d9579ae83a3d9e38c140371e4c6b038136909235079f2e6e78/wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", size = 37523 }, + { url = "https://files.pythonhosted.org/packages/92/17/224132494c1e23521868cdd57cd1e903f3b6a7ba6996b7b8f077ff8ac7fe/wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", size = 37614 }, + { url = "https://files.pythonhosted.org/packages/6a/d7/cfcd73e8f4858079ac59d9db1ec5a1349bc486ae8e9ba55698cc1f4a1dff/wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", size = 38316 }, + { url = "https://files.pythonhosted.org/packages/7e/79/5ff0a5c54bda5aec75b36453d06be4f83d5cd4932cc84b7cb2b52cee23e2/wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", size = 86322 }, + { url = "https://files.pythonhosted.org/packages/c4/81/e799bf5d419f422d8712108837c1d9bf6ebe3cb2a81ad94413449543a923/wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", size = 79055 }, + { url = "https://files.pythonhosted.org/packages/62/62/30ca2405de6a20448ee557ab2cd61ab9c5900be7cbd18a2639db595f0b98/wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", size = 87291 }, + { url = "https://files.pythonhosted.org/packages/49/4e/5d2f6d7b57fc9956bf06e944eb00463551f7d52fc73ca35cfc4c2cdb7aed/wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", size = 90374 }, + { url = "https://files.pythonhosted.org/packages/a6/9b/c2c21b44ff5b9bf14a83252a8b973fb84923764ff63db3e6dfc3895cf2e0/wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", size = 83896 }, + { url = "https://files.pythonhosted.org/packages/14/26/93a9fa02c6f257df54d7570dfe8011995138118d11939a4ecd82cb849613/wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", size = 91738 }, + { url = "https://files.pythonhosted.org/packages/a2/5b/4660897233eb2c8c4de3dc7cefed114c61bacb3c28327e64150dc44ee2f6/wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", size = 35568 }, + { url = "https://files.pythonhosted.org/packages/5c/cc/8297f9658506b224aa4bd71906447dea6bb0ba629861a758c28f67428b91/wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", size = 37653 }, + { url = "https://files.pythonhosted.org/packages/70/cc/b92e1da2cad6a9f8ee481000ece07a35e3b24e041e60ff8b850c079f0ebf/wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", size = 37314 }, + { url = "https://files.pythonhosted.org/packages/4a/cc/3402bcc897978be00fef608cd9e3e39ec8869c973feeb5e1e277670e5ad2/wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", size = 38162 }, + { url = "https://files.pythonhosted.org/packages/28/d3/4f079f649c515727c127c987b2ec2e0816b80d95784f2d28d1a57d2a1029/wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", size = 80235 }, + { url = "https://files.pythonhosted.org/packages/a3/1c/226c2a4932e578a2241dcb383f425995f80224b446f439c2e112eb51c3a6/wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", size = 72553 }, + { url = "https://files.pythonhosted.org/packages/b1/e7/459a8a4f40f2fa65eb73cb3f339e6d152957932516d18d0e996c7ae2d7ae/wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", size = 80129 }, + { url = "https://files.pythonhosted.org/packages/da/6f/6d0b3c4983f1fc764a422989dabc268ee87d937763246cd48aa92f1eed1e/wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", size = 84550 }, + { url = "https://files.pythonhosted.org/packages/96/e8/27ef35cf61e5147c1c3abcb89cfbb8d691b2bb8364803fcc950140bc14d8/wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", size = 77352 }, + { url = "https://files.pythonhosted.org/packages/b6/ad/7a0766341081bfd9f18a7049e4d6d45586ae5c5bb0a640f05e2f558e849c/wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", size = 84626 }, + { url = "https://files.pythonhosted.org/packages/09/43/b26852e9c45a1aac0d14b1080b25b612fa840ba99739c5fc55db07b7ce08/wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", size = 35327 }, + { url = "https://files.pythonhosted.org/packages/74/f2/96ed140b08743f7f68d5bda35a2a589600781366c3da96f056043d258b1a/wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", size = 37526 }, + { url = "https://files.pythonhosted.org/packages/ff/21/abdedb4cdf6ff41ebf01a74087740a709e2edb146490e4d9beea054b0b7a/wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", size = 23362 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +]