generic-orig-1.1.7/0000775000175000017500000000000015153546432013052 5ustar hasanhasangeneric-orig-1.1.7/CODE_OF_CONDUCT.md0000664000175000017500000001213615153546432015654 0ustar hasanhasan # 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, 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 dan@yeaw.me. 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.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. generic-orig-1.1.7/CONTRIBUTING.md0000664000175000017500000001314315153546432015305 0ustar hasanhasan# Contributing ### We :heart: our Contributors! First off, thank you for considering contributing to Generic. It's people like you that make Generic such a great library. ### Why a Guideline? Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests. ### What we are Looking For Generic is an open source project and we love to receive contributions from our community β€” you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into Generic itself. ### What we are not Looking For Please, don't use the issue tracker for support questions. Check whether the your question can be answered on the [Gaphor Gitter Channel](https://gitter.im/gaphor/Lobby). # Ground Rules ### Responsibilities * Ensure cross-platform compatibility for every change that's accepted. Windows, Mac, Debian & Ubuntu Linux. * Ensure that code that goes into core meets all requirements in this [PR Review Checklist](https://gist.github.com/audreyr/4feef90445b9680475f2). * Create issues for any major changes and enhancements that you wish to make. * Discuss things transparently and get community feedback. * Don't add any classes to the codebase unless absolutely needed. Err on the side of using functions. * Keep feature versions as small as possible, preferably one new feature per version. * Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/). # Your First Contribution Unsure where to begin contributing to Generic? You can start by looking through these `first-timers-only` and `up-for-grabs` issues: * [First-timers-only issues](https://github.com/gaphor/generic/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Afirst-timers-only) - issues which should only require a few lines of code, and a test or two. * [Up-for-grabs issues](https://github.com/gaphor/generic/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Aup-for-grabs) - issues which should be a bit more involved than `first-timers-only` issues. ### Is This Your First Open Source Contribution? Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). At this point, you're ready to make your changes! Feel free to ask for help; everyone is a beginner at first :smile_cat: If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your branch so it's easier to merge. # Getting Started For something that is bigger than a one or two line fix: 1. Create your own fork of the code 2. Install all development dependencies using: ``` $ poetry install $ pre-commit install ``` If you haven't used poetry before, just run `pip install poetry`, and then run the commands above, it will do the correct thing. 3. Add tests for your changes, run the tests with `pytest`. 4. Do the changes in your fork. 5. If you like the change and think the project could use it: * Be sure you have the pre-commit hook installed above, it will ensure that [Ruff](https://docs.astral.sh/ruff/) is automatically run on any changes for consistent code formatting. * [Sign](https://help.github.com/articles/signing-commits/) your commits. * Note the Generic Code of Conduct. * Create a pull request. Small contributions such as fixing spelling errors, where the content is small enough to not be considered intellectual property, can be submitted by a contributor as a patch, without signing your commit. As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following: * Spelling / grammar fixes * Typo correction, white space and formatting changes * Comment clean up * Bug fixes that change default return values or error codes stored in constants * Adding logging messages or debugging output * Changes to β€˜metadata’ files like pyproject.toml, .gitignore, build scripts, etc. * Moving source files from one directory or package to another # How to Report a Bug If you find a security vulnerability, do NOT open an issue. Email dan@yeaw.me instead. When filing an issue, make sure to answer the questions in the issue template. 1. What version are you using? 2. What operating system are you using? 3. What did you do? 4. What did you expect to see? 5. What did you see instead? # How to Suggest a Feature or Enhancement If you find yourself wishing for a feature that doesn't exist in Generic, you are probably not alone. There are bound to be others out there with similar needs. Many of the features that Generic has today have been added because our users saw the need. Open an issue on our issues list on GitHub which describes the feature you would like to see, why you need it, and how it should work. # Code review process The core team looks at Pull Requests on a regular basis, you should expect a response within a week. After feedback has been given we expect responses within two weeks. After two weeks we may close the pull request if it isn't showing any activity. # Community You can chat with the Generic community on gitter: https://gitter.im/Gaphor/Lobby. generic-orig-1.1.7/SECURITY.md0000664000175000017500000000104115153546432014637 0ustar hasanhasan# Security Policy ## Supported Versions We are currently supporting the latest released version of the library. ## Reporting a Vulnerability Generic has GitHub's Private Security Vulnerability Reporting enabled. Please go to the Security tab to report security vulnerabilities. For more information, please see the [GitHub docs on privately reporting]( https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). generic-orig-1.1.7/.readthedocs.yml0000664000175000017500000000056715153546432016150 0ustar hasanhasanversion: 2 formats: all build: os: ubuntu-24.04 tools: python: "3.13" jobs: pre_install: - python -m pip install --constraint=.github/github-requirements.txt poetry - poetry config virtualenvs.create false post_install: - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs --no-interaction sphinx: configuration: docs/conf.py generic-orig-1.1.7/.pre-commit-config.yaml0000664000175000017500000000102115153546432017325 0ustar hasanhasanrepos: - repo: https://github.com/pre-commit/mirrors-mypy rev: a66e98df7b4aeeb3724184b332785976d062b92e # frozen: v1.19.1 hooks: - id: mypy - repo: https://github.com/pre-commit/pre-commit-hooks rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - id: check-toml - id: check-yaml - id: end-of-file-fixer - repo: https://github.com/astral-sh/ruff-pre-commit rev: 'a27a2e47c7751b639d2b5badf0ef6ff11fee893f' # frozen: v0.15.4 hooks: - id: ruff args: [--fix] - id: ruff-format generic-orig-1.1.7/.github/0000775000175000017500000000000015153546432014412 5ustar hasanhasangeneric-orig-1.1.7/.github/ISSUE_TEMPLATE/0000775000175000017500000000000015153546432016575 5ustar hasanhasangeneric-orig-1.1.7/.github/ISSUE_TEMPLATE/bug_report.md0000664000175000017500000000123015153546432021263 0ustar hasanhasan--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **OS** - [ ] Linux (Please put in notes the specific distro) - [ ] MacOS - [ ] Windows NOTES: **Version** Version of Generic: **Additional context** Add any other context about the problem here. generic-orig-1.1.7/.github/ISSUE_TEMPLATE/feature_request.md0000664000175000017500000000112315153546432022317 0ustar hasanhasan--- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. generic-orig-1.1.7/.github/pr-labeler.yml0000664000175000017500000000017515153546432017165 0ustar hasanhasanfeature: ['feature/*', 'feat/*'] fix: fix/* chore: chore/* skip-changelog: ['sourcery/*', 'dependabot/*', 'pre-commit-ci-*'] generic-orig-1.1.7/.github/dependabot.yml0000664000175000017500000000062415153546432017244 0ustar hasanhasanversion: 2 updates: - package-ecosystem: "github-actions" directory: "/" groups: github-action-updates: patterns: - "*" schedule: interval: weekly labels: ["skip-changelog"] - package-ecosystem: "pip" directory: "/" groups: pip-updates: patterns: - "*" schedule: interval: weekly labels: ["skip-changelog"] generic-orig-1.1.7/.github/PULL_REQUEST_TEMPLATE.md0000664000175000017500000000205615153546432020216 0ustar hasanhasan ### PR Checklist Please check if your PR fulfills the following requirements: - [ ] I have read, and I am following the [Contributor guide](https://github.com/gaphor/generic/blob/main/CONTRIBUTING.md) - [ ] I have read and understand the [Code of Conduct](https://github.com/gaphor/generic/blob/main/CODE_OF_CONDUCT.md) ### PR Type What kind of change does this PR introduce? - [ ] Bugfix - [ ] Feature - [ ] Code style update (formatting, local variables) - [ ] Refactoring (no functional changes, no api changes) - [ ] Documentation content changes ### What is the current behavior? Issue Number: N/A ### What is the new behavior? ### Does this PR introduce a breaking change? - [ ] Yes - [ ] No ### Other information generic-orig-1.1.7/.github/release-drafter.yml0000664000175000017500000000136315153546432020205 0ustar hasanhasanname-template: 'Version $NEXT_PATCH_VERSION - Summary Here🌈' tag-template: 'v$NEXT_PATCH_VERSION' exclude-labels: - 'skip-changelog' categories: - title: 'πŸš€ Features' labels: - 'feature' - 'enhancement' - title: 'πŸ› Bug Fixes' labels: - 'fix' - 'bugfix' - 'bug' - title: '🧰 Maintenance' label: 'chore' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' template: | ## Changes $CHANGES Thanks again to $CONTRIBUTORS! πŸŽ‰ no-changes-template: 'Changes are coming soon 😎' replacers: - search: '(?:and )?@dependabot(?:\[bot\])?,?' replace: '' - search: '(?:and )?@sourcery-ai-bot(?:\[bot\])?,?' replace: '' - search: '(?:and )?@allcontributors(?:\[bot\])?,?' replace: '' generic-orig-1.1.7/.github/github-requirements.txt0000664000175000017500000000004015153546432021150 0ustar hasanhasanpoetry==2.3.2 pre-commit==4.5.1 generic-orig-1.1.7/.github/workflows/0000775000175000017500000000000015153546432016447 5ustar hasanhasangeneric-orig-1.1.7/.github/workflows/pre-commit-updater.yml0000664000175000017500000000374215153546432022716 0ustar hasanhasanname: Pre-commit updater on: workflow_dispatch: schedule: # min hour dom month dow - cron: '0 5 * * 3' env: python_version: '3.13' jobs: updater: name: Update runs-on: ubuntu-24.04 steps: - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > files.pythonhosted.org:443 pypi.org:443 github.com:443 api.github.com:443 *.githubusercontent.com:443 ghcr.io - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: main - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.python_version }} cache: pip - name: Install pre-commit run: python -m pip install pre-commit - name: Update pre-commit hooks run: pre-commit autoupdate --freeze - name: Run pre-commit hooks run: pre-commit run --all-files - name: Create GitHub App Token uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 id: generate-token with: app-id: ${{ secrets.GAPHOR_UPDATER_APP_ID }} private-key: ${{ secrets.GAPHOR_UPDATER_APP_PRIVATE_KEY }} - name: Create Pull Request uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 with: token: ${{ steps.generate-token.outputs.token }} commit-message: Update pre-commit hooks branch: pre-commit-update delete-branch: true title: 'Update pre-commit hooks' body: | This PR was automatically created to make the following update: - Update pre-commit hooks labels: | skip-changelog generic-orig-1.1.7/.github/workflows/dependency-review.yml0000664000175000017500000000171215153546432022610 0ustar hasanhasan# 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 name: 'Dependency Review' on: [pull_request] permissions: contents: read jobs: dependency-review: runs-on: ubuntu-24.04 steps: - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: egress-policy: audit - name: 'Checkout Repository' uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 'Dependency Review' uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3 generic-orig-1.1.7/.github/workflows/pr-labeler.yml0000664000175000017500000000117515153546432021223 0ustar hasanhasanname: PR Labeler on: pull_request_target: types: [opened] permissions: contents: read jobs: pr-labeler: permissions: pull-requests: write # for TimonVS/pr-labeler-action to add labels in PR runs-on: ubuntu-24.04 if: "!contains(github.event.head_commit.message, 'skip ci')" steps: - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: egress-policy: audit - uses: TimonVS/pr-labeler-action@f9c084306ce8b3f488a8f3ee1ccedc6da131d1af # v5.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} generic-orig-1.1.7/.github/workflows/release-drafter.yml0000664000175000017500000000145715153546432022246 0ustar hasanhasanname: Release Drafter on: push: branches: main permissions: contents: read jobs: update-release-draft: permissions: contents: write # for release-drafter/release-drafter to create a github release pull-requests: write # for release-drafter/release-drafter to add label to PR runs-on: ubuntu-24.04 if: "!contains(github.event.head_commit.message, 'skip ci')" steps: # Drafts your next Release notes as Pull Requests are merged into "main" - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: egress-policy: audit - uses: release-drafter/release-drafter@6db134d15f3909ccc9eefd369f02bd1e9cffdf97 # v6.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} generic-orig-1.1.7/.github/workflows/codeql.yml0000664000175000017500000000177615153546432020454 0ustar hasanhasanname: "CodeQL" on: push: branches: ["main"] pull_request: # The branches below must be a subset of the branches above branches: ["main"] schedule: - cron: "0 0 * * 1" permissions: contents: read jobs: analyze: name: Analyze runs-on: ubuntu-24.04 permissions: actions: read contents: read security-events: write steps: - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: egress-policy: audit - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 with: languages: actions,python - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 generic-orig-1.1.7/.github/workflows/scorecard.yml0000664000175000017500000000270315153546432021141 0ustar hasanhasanname: Scorecard supply-chain security on: branch_protection_rule: schedule: - cron: '17 12 * * 6' push: branches: [ "main" ] # Declare default permissions as read only. permissions: read-all jobs: analysis: name: Scorecard analysis runs-on: ubuntu-24.04 permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write steps: - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: egress-policy: audit - name: "Checkout code" uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif publish_results: true - name: "Upload artifact" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: SARIF file path: results.sarif retention-days: 5 - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 with: sarif_file: results.sarif generic-orig-1.1.7/.github/workflows/build.yml0000664000175000017500000001247115153546432020276 0ustar hasanhasanname: build on: push: branches: - main paths-ignore: - '*.md' pull_request: branches: - main release: types: [published] workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: python-version: '3.14' permissions: contents: read jobs: lint: runs-on: ubuntu-24.04 permissions: contents: read steps: - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > files.pythonhosted.org:443 pypi.org:443 github.com:443 api.github.com:443 *.githubusercontent.com:443 ghcr.io - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.event.pull_request.head.sha }} - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.python-version }} - name: Lint with Pre-commit run: pipx run --pip-args='--constraint=.github/github-requirements.txt' pre-commit run --all-files - name: Check REUSE compliance run: pip install reuse && python -m reuse lint - name: Check Poetry lock file integrity run: | python${{ env.python-version }} -m pip install --constraint=.github/github-requirements.txt poetry poetry config virtualenvs.in-project true poetry check build: needs: lint runs-on: ubuntu-24.04 permissions: contents: write strategy: max-parallel: 4 matrix: python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] name: build (python ${{ matrix.python-version }}) outputs: targz: generic-${{ steps.meta.outputs.version }}.tar.gz wheel: generic-${{ steps.meta.outputs.version }}-py3-none-any.whl steps: - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: disable-sudo: true egress-policy: block allowed-endpoints: > qlty.sh/d/coverage:443 qlty-releases.s3.amazonaws.com:443 files.pythonhosted.org:443 pypi.org:443 github.com:443 *.githubusercontent.com:443 ghcr.io keys.openpgp.org:443 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Install Poetry run: | python${{ matrix.python-version }} -m pip install --constraint=.github/github-requirements.txt poetry poetry config virtualenvs.in-project true - name: Collect Project Data id: meta run: .github/scripts/metadata.sh - name: Install dependencies run: poetry install --no-interaction - name: Test run: | poetry run pytest --cov=generic poetry run coverage lcov - name: Upload Code Coverage to Qlty.sh uses: qltysh/qlty-action/coverage@a19242102d17e497f437d7466aa01b528537e899 # v2.2.0 with: token: ${{ secrets.QLTY_COVERAGE_TOKEN }} files: coverage.lcov - name: Create Source Dist and Wheel if: ${{ matrix.python-version == env.python-version }} run: poetry build - name: Upload generic-${{ steps.meta.outputs.version }}.tar.gz if: ${{ matrix.python-version == env.python-version }} uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: generic-${{ steps.meta.outputs.version }}.tar.gz path: dist/generic-${{ steps.meta.outputs.version }}.tar.gz - name: Upload generic-${{ steps.meta.outputs.version }}-py3-none-any.whl if: ${{ matrix.python-version == env.python-version }} uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: generic-${{ steps.meta.outputs.version }}-py3-none-any.whl path: dist/generic-${{ steps.meta.outputs.version }}-py3-none-any.whl publish-to-pypi: name: Publish to PyPI (release only) needs: build runs-on: ubuntu-24.04 permissions: id-token: write if: ${{ github.event_name == 'release' }} steps: - name: Harden Runner uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: egress-policy: audit - name: Download tar.gz uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: name: ${{ needs.build.outputs.targz }} path: dist - name: Download wheel uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: name: ${{ needs.build.outputs.wheel }} path: dist - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1 generic-orig-1.1.7/.github/scripts/0000775000175000017500000000000015153546432016101 5ustar hasanhasangeneric-orig-1.1.7/.github/scripts/metadata.sh0000775000175000017500000000121215153546432020214 0ustar hasanhasan#!/bin/bash echo "GITHUB_REF is $GITHUB_REF" TAG="${GITHUB_REF/refs\/tags\//}" echo "TAG is $TAG" if ! [ -x "$(command -v poetry)" ]; then echo 'Poetry not found!' >&2 exit 1 fi VERSION="$(poetry version --no-ansi | cut -d' ' -f2)" echo "VERSION is $VERSION" if [[ "$GITHUB_REF" =~ refs\/tags\/.* && "$TAG" == "${VERSION}" ]] then REV="" RELEASE="true" else # PEP440 version scheme, different from semver 2.0 REV=".dev${GITHUB_RUN_NUMBER:-0}+${GITHUB_SHA:0:8}" RELEASE="false" poetry version "${VERSION}""${REV}" fi echo "version=${VERSION}${REV}" >> "$GITHUB_OUTPUT" echo "release=${RELEASE}" >> "$GITHUB_OUTPUT" generic-orig-1.1.7/docs/0000775000175000017500000000000015153546432014002 5ustar hasanhasangeneric-orig-1.1.7/docs/registry.rst0000664000175000017500000000002215153546432016376 0ustar hasanhasanRegistry ======== generic-orig-1.1.7/docs/Makefile0000664000175000017500000001270315153546432015445 0ustar hasanhasan# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -a SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/generic.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/generic.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/generic" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/generic" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." generic-orig-1.1.7/docs/conf.py0000664000175000017500000001777115153546432015316 0ustar hasanhasan# # generic documentation build configuration file, created by # sphinx-quickstart on Sat Dec 3 16:59:36 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import importlib.metadata import sys from pathlib import Path project_dir = Path(__file__).resolve().parent.parent sys.path.insert(0, str(project_dir)) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "generic" copyright = "2011, Andrey Popp, 2019, Arjan Molenaar and Dan Yeaw" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. version = importlib.metadata.version("generic") # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. # pygments_style = "bw" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "furo" html_title = f"Generic v{version}" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [pkg_resources.resource_filename("bw_sphinxtheme", "themes")] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "genericdoc" # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # type: ignore[var-annotated] # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ("index", "generic.tex", "generic Documentation", "Andrey Popp", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [("index", "generic", "generic Documentation", ["Andrey Popp"], 1)] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( "index", "generic", "generic Documentation", "Andrey Popp", "generic", "One line description of project.", "Miscellaneous", ), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} generic-orig-1.1.7/docs/index.rst0000664000175000017500000000205715153546432015647 0ustar hasanhasanGeneric programming library for Python ====================================== Generic is trying to provide a Python programmer with primitives for creating reusable software components by employing advanced techniques of OOP and other programming paradigms. This documentation suits both needs in a tutorial and an API reference for generic: .. toctree:: :maxdepth: 3 :hidden: multidispatching event_system registry Installation ------------ You can get generic with *pip*:: % python -m pip install generic In case you find a bug or have a feature request, please file a ticket at `GitHub Issues`_. .. _GitHub Issues: https://github.com/gaphor/generic/issues Development process ------------------- Development takes place at `GitHub`_, you can clone the source code repository with the following command:: % git clone git://github.com/gaphor/generic.git We love contributions! If you are submitting a GitHub pull request please ensure you have tests for your bugfix or new functionality. .. _GitHub: https://github.com/gaphor/generic generic-orig-1.1.7/docs/multidispatching.rst0000664000175000017500000001452015153546432020106 0ustar hasanhasanMultidispatching ================ Multidispatching allows you to define methods and functions which should behave differently based on arguments' types without cluttering ``if-elif-else`` chains and ``isinstance`` calls. All you need is inside ``generic.multidispatch`` module. See examples below to learn how to use it to define multifunctions and multimethods. First the basics: >>> class Cat: pass >>> class Dog: pass >>> class Duck: pass Multifunctions -------------- Suppose we want to define a function which behaves differently based on arguments' types. The naive solution is to inspect argument types with ``isinstance`` function calls but generic provides us with ``@multidispatch`` decorator which can easily reduce the amount of boilerplate and provide desired level of extensibility:: >>> from generic.multidispatch import multidispatch >>> @multidispatch(Dog) ... def sound(o): ... print("Woof!") >>> @sound.register(Cat) ... def cat_sound(o): ... print("Meow!") Each separate definition of ``sound`` function works for different argument types, we will call each such definition *a multifunction case* or simply *a case*. We can test if our ``sound`` multifunction works as expected:: >>> sound(Dog()) Woof! >>> sound(Cat()) Meow! >>> sound(Duck()) # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: No available rule found for ... The main advantage of using multifunctions over single function with a bunch of ``isinstance`` checks is extensibility -- you can add more cases for other types even in separate module:: >>> @sound.register(Duck) ... def duck_sound(o): ... print("Quack!") When behaviour of multifunction depends on some argument we will say that this multifunction *dispatches* on this argument. Multifunctions of several arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can also define multifunctions of several arguments and even decide on which of first arguments you want to dispatch. For example the following function will only dispatch on its first argument while requiring both of them:: >>> @multidispatch(Dog) ... def walk(dog, meters): ... print("Dog walks for %d meters" % meters) But sometimes you want multifunctions to dispatch on more than one argument, then you just have to provide several arguments to ``multidispatch`` decorator and to subsequent ``when`` decorators:: >>> @multidispatch(Dog, Cat) ... def chases(dog, cat): ... return True >>> @chases.register(Dog, Dog) ... def chases_dog_dog(dog1, dog2): ... return None >>> @chases.register(Cat, Dog) ... def chases_cat_dog(cat, dog): ... return False You can have any number of arguments to dispatch on but they should be all positional, keyword arguments are allowed for multifunctions only if they're not used for dispatch. Multimethods ------------ Another functionality provided by ``generic.multimethod`` module are *multimethods*. Multimethods are similar to multifunctions except they are... methods. Technically the main and the only difference between multifunctions and multimethods is the latter is also dispatch on ``self`` argument. Implementing multimethods is similar to implementing multifunctions, you just have to decorate your methods with ``multimethod`` decorator instead of ``multidispatch``. But there's some issue with how Python's classes works which forces us to use also ``has_multimethods`` class decorator:: >>> class Vegetable: pass >>> class Meat: pass >>> from generic.multimethod import multimethod, has_multimethods >>> @has_multimethods ... class Animal(object): ... ... @multimethod(Vegetable) ... def can_eat(self, food): ... return True ... ... @can_eat.register(Meat) ... def can_eat(self, food): ... return False register rule (, ) register rule (, ) This would work like this:: >>> animal = Animal() >>> animal.can_eat(Vegetable()) True >>> animal.can_eat(Meat()) False So far we haven't seen any differences between multifunctions and multimethods but as it have already been said there's one -- multimethods use ``self`` argument for dispatch. We can see that if we would subclass our ``Animal`` class and override ``can_eat`` method definition:: >>> @has_multimethods ... class Predator(Animal): ... @Animal.can_eat.register(Meat) ... def can_eat(self, food): ... return True register rule (, ) This will override ``can_eat`` on ``Predator`` instances but *only* for the case for ``Meat`` argument, case for the ``Vegetable`` is not overridden, so class inherits it from ``Animal``:: >>> predator = Predator() >>> predator.can_eat(Vegetable()) True >>> predator.can_eat(Meat()) True The only thing to care is you should not forget to include ``@has_multimethods`` decorator on classes which define or override multimethods. You can also provide a "catch-all" case for multimethod using ``otherwise`` decorator just like in example for multifunctions. Providing "catch-all" case ~~~~~~~~~~~~~~~~~~~~~~~~~~ There should be an analog to ``else`` statement -- a case which is used when no matching case is found, we will call such case *a catch-all case*, here is how you can define it using ``otherwise`` decorator:: >>> @has_multimethods ... class Animal(object): ... ... @multimethod(Vegetable) ... def can_eat(self, food): ... return True ... ... @can_eat.register(Meat) ... def can_eat(self, food): ... return False ... ... @can_eat.otherwise ... def can_eat(self, food): ... return "?" register rule (, ) register rule (, ) register rule (, ) >>> Animal().can_eat(1) '?' You can try calling ``sound`` with whatever argument type you wish, it will never fall with ``TypeError`` anymore. API reference ------------- .. autofunction:: generic.multidispatch.multidispatch .. autofunction:: generic.multimethod.multimethod .. autofunction:: generic.multimethod.has_multimethods .. autoclass:: generic.multidispatch.FunctionDispatcher :members: register .. autoclass:: generic.multimethod.MethodDispatcher :members: register, otherwise generic-orig-1.1.7/docs/event_system.rst0000664000175000017500000000251615153546432017265 0ustar hasanhasanEvent system ============ Generic library provides ``generic.event`` module which helps you implement event systems in your application. By event system I mean an API for *subscribing* for some types of events and to *handle* those events so previously subscribed *handlers* are being executed. Basic usage ----------- First you need to describe event types you want to use in your application, ``generic.event`` dispatches events to corresponding handlers by inspecting events' types, so it's natural to model those as classes:: >>> class CommentAdded(object): ... def __init__(self, post_id, comment): ... self.post_id = post_id ... self.comment = comment Now you want to register handler for your event type:: >>> from generic.event import Manager >>> manager = Manager() >>> @manager.subscriber(CommentAdded) ... def print_comment(ev): ... print(f"Got new comment: {ev.comment}") Then you just call ``generic.event.handle`` function with ``CommentAdded`` instance as its argument:: >>> manager.handle(CommentAdded(167, "Hello!")) Got new comment: Hello! This is how it works. Event inheritance ----------------- Using per-application event API ------------------------------- API reference ------------- .. autoclass:: generic.event.Manager :members: subscribe, subscriber, handle, unsubscribe generic-orig-1.1.7/generic/0000775000175000017500000000000015153546432014466 5ustar hasanhasangeneric-orig-1.1.7/generic/multimethod.py0000664000175000017500000000673115153546432017402 0ustar hasanhasan"""Multi-method builds on the functionality provided by `multidispatch` to provide generic methods.""" from __future__ import annotations import functools import inspect import logging import threading import types from typing import Any, Callable, TypeVar, Union, cast from generic.multidispatch import FunctionDispatcher, KeyType __all__ = ("multimethod", "has_multimethods") C = TypeVar("C") T = TypeVar("T", bound=Union[Callable[..., Any], type]) logger = logging.getLogger(__name__) def multimethod(*argtypes: KeyType) -> Callable[[T], MethodDispatcher[T]]: """Declare method as multimethod. This decorator works exactly the same as :func:`.multidispatch` decorator but replaces decorated method with :class:`.MethodDispatcher` object instead. Should be used only for decorating methods and enclosing class should have :func:`.has_multimethods` decorator. """ def _replace_with_dispatcher(func): nonlocal argtypes argspec = inspect.getfullargspec(func) dispatcher = cast( MethodDispatcher, functools.update_wrapper( MethodDispatcher(argspec, len(argtypes) + 1), func ), ) dispatcher.register_unbound_rule(func, *argtypes) return dispatcher return _replace_with_dispatcher def has_multimethods(cls: type[C]) -> type[C]: """Declare class as one that have multimethods. Should only be used for decorating classes which have methods decorated with :func:`.multimethod` decorator. """ for _name, obj in cls.__dict__.items(): if isinstance(obj, MethodDispatcher): obj.proceed_unbound_rules(cls) return cls class MethodDispatcher(FunctionDispatcher[T]): """Multiple dispatch for methods. This object dispatch call to method by its class and arguments types. Usually it is produced by :func:`.multimethod` decorator. You should not manually create objects of this type. """ def __init__(self, argspec: inspect.FullArgSpec, params_arity: int) -> None: super().__init__(argspec, params_arity) # some data, that should be local to thread of execution self.local = threading.local() self.local.unbound_rules = [] def register_unbound_rule(self, func, *argtypes) -> None: """Register unbound rule that should be processed by ``proceed_unbound_rules`` later.""" self.local.unbound_rules.append((argtypes, func)) def proceed_unbound_rules(self, cls) -> None: """Process all unbound rule by binding them to ``cls`` type.""" for argtypes, func in self.local.unbound_rules: argtypes = (cls,) + argtypes logger.debug("register rule %s", argtypes) self.register_rule(func, *argtypes) self.local.unbound_rules = [] def __get__(self, obj, cls): return self if obj is None else types.MethodType(self, obj) def register(self, *argtypes: KeyType) -> Callable[[T], T]: """Register new case for multimethod for ``argtypes``""" def make_declaration(meth): self.register_unbound_rule(meth, *argtypes) return self return make_declaration @property def otherwise(self) -> Callable[[T], T]: """Decorator which registers "catch-all" case for multimethod.""" def make_declaration(meth): self.register_unbound_rule(meth, *([object] * (self.params_arity - 1))) return self return make_declaration generic-orig-1.1.7/generic/event.py0000664000175000017500000000546215153546432016170 0ustar hasanhasan"""Event management system. This module provides API for event management. """ from sys import version_info from typing import Callable, Set, Type if version_info < (3, 11): from exceptiongroup import ExceptionGroup from generic.registry import Registry, TypeAxis __all__ = "Manager" Event = object Handler = Callable[[object], None] HandlerSet = Set[Handler] class Manager: """Event manager. Provides API for subscribing for and firing events. """ registry: Registry[HandlerSet] def __init__(self) -> None: axes = (("event_type", TypeAxis()),) self.registry = Registry(*axes) def subscribe(self, handler: Handler, event_type: Type[Event]) -> None: """Subscribe ``handler`` to specified ``event_type``""" handler_set = self.registry.get_registration(event_type) if handler_set is None: handler_set = self._register_handler_set(event_type) handler_set.add(handler) def unsubscribe(self, handler: Handler, event_type: Type[Event]) -> None: """Unsubscribe ``handler`` from ``event_type``""" handler_set = self.registry.get_registration(event_type) if handler_set and handler in handler_set: handler_set.remove(handler) def handle(self, event: Event) -> None: """Fire ``event`` All subscribers will be executed with no determined order. If a handler raises an exceptions, an `ExceptionGroup` will be raised containing all raised exceptions. """ handler_sets = self.registry.query(event) for handler_set in handler_sets: if handler_set: exceptions = [] for handler in set(handler_set): try: handler(event) except BaseException as e: exceptions.append(e) if exceptions: raise ExceptionGroup("Error while handling events", exceptions) def _register_handler_set(self, event_type: Type[Event]) -> HandlerSet: """Register new handler set for ``event_type``.""" handler_set: HandlerSet = set() self.registry.register(handler_set, event_type) return handler_set def subscriber(self, event_type: Type[Event]) -> Callable[[Handler], Handler]: """Decorator for subscribing handlers. Works like this: >>> mymanager = Manager() >>> class MyEvent(): ... pass >>> @mymanager.subscriber(MyEvent) ... def mysubscriber(evt): ... # handle event ... return >>> mymanager.handle(MyEvent()) """ def registrator(func: Handler) -> Handler: self.subscribe(func, event_type) return func return registrator generic-orig-1.1.7/generic/registry.py0000664000175000017500000001162315153546432016713 0ustar hasanhasan"""Registry.""" from __future__ import annotations from typing import ( Any, Dict, Generator, Generic, KeysView, Sequence, TypeVar, Union, Iterator, ) __all__ = ("Registry", "SimpleAxis", "TypeAxis") K = TypeVar("K") S = TypeVar("S") T = TypeVar("T") V = TypeVar("V") Axis = Union["SimpleAxis", "TypeAxis"] class Registry(Generic[T]): """Registry implementation.""" def __init__(self, *axes: tuple[str, Axis]): self._tree: _TreeNode[T] = _TreeNode() self._axes = [axis for name, axis in axes] self._axes_dict = {name: (i, axis) for i, (name, axis) in enumerate(axes)} def register(self, target: T, *arg_keys: K, **kw_keys: K) -> None: tree_node = self._tree for key in self._align_with_axes(arg_keys, kw_keys): tree_node = tree_node.setdefault(key, _TreeNode()) if tree_node.target is not None: raise ValueError( f"Registration for {target} conflicts with existing registration {tree_node.target}." ) tree_node.target = target def get_registration(self, *arg_keys: K, **kw_keys: K) -> T | None: tree_node = self._tree for key in self._align_with_axes(arg_keys, kw_keys): if key not in tree_node: return None tree_node = tree_node[key] return tree_node.target def lookup(self, *arg_objs: V, **kw_objs: V) -> T | None: return next(self.query(*arg_objs, **kw_objs), None) def query(self, *arg_objs: V, **kw_objs: V) -> Iterator[T | None]: objs = self._align_with_axes(arg_objs, kw_objs) return filter(None, self._query(self._tree, objs, self._axes)) def _query( self, tree_node: _TreeNode[T], objs: Sequence[V | None], axes: Sequence[Axis] ) -> Generator[T | None, None, None]: """Recursively traverse registration tree, from left to right, most specific to least specific, returning the first target found on a matching node.""" if not objs: yield tree_node.target else: obj = objs[0] # Skip non-participating nodes if obj is None: next_node: _TreeNode[T] | None = tree_node.get(None, None) if next_node is not None: yield from self._query(next_node, objs[1:], axes[1:]) else: # Get matches on this axis and iterate from most to least specific axis = axes[0] for match_key in axis.matches(obj, tree_node.keys()): yield from self._query(tree_node[match_key], objs[1:], axes[1:]) def _align_with_axes( self, args: Sequence[S], kw: dict[str, S] ) -> Sequence[S | None]: """Create a list matching up all args and kwargs with their corresponding axes, in order, using ``None`` as a placeholder for skipped axes.""" axes_dict = self._axes_dict aligned: list[S | None] = [None for _ in range(len(axes_dict))] args_len = len(args) if args_len + len(kw) > len(aligned): raise ValueError("Cannot have more arguments than axes.") for i, arg in enumerate(args): aligned[i] = arg for k, v in kw.items(): i_axis = axes_dict.get(k, None) if i_axis is None: raise ValueError(f"No axis with name: {k}") i, _axis = i_axis if aligned[i] is not None: raise ValueError( "Axis defined twice between positional and keyword arguments" ) aligned[i] = v # Trim empty tail nodes for faster look-ups while aligned and aligned[-1] is None: del aligned[-1] return aligned class _TreeNode(Generic[T], Dict[Any, Any]): target: T | None = None def __str__(self) -> str: return f"" class SimpleAxis: """A simple axis where the key into the axis is the same as the object to be matched (aka the identity axis). This axis behaves just like a dictionary. You might use this axis if you are interested in registering something by name, where you're registering an object with the string that is the name and then using the name to look it up again later. Subclasses can override the ``get_keys`` method for implementing arbitrary axes. """ def matches( self, obj: object, keys: KeysView[object | None] ) -> Generator[object, None, None]: for key in [obj]: if key in keys: yield obj class TypeAxis: """An axis which matches the class and super classes of an object in method resolution order.""" def matches( self, obj: object, keys: KeysView[type | None] ) -> Generator[type, None, None]: for key in type(obj).mro(): if key in keys: yield key generic-orig-1.1.7/generic/__init__.py0000664000175000017500000000000015153546432016565 0ustar hasanhasangeneric-orig-1.1.7/generic/multidispatch.py0000664000175000017500000001130015153546432017705 0ustar hasanhasan"""Multidispatch for functions and methods. This code is a Python 3, slimmed down version of the generic package by Andrey Popp. """ from __future__ import annotations import functools import inspect import logging from typing import Any, Callable, Generic, TypeVar, Union, cast from generic.registry import Registry, TypeAxis __all__ = "multidispatch" T = TypeVar("T", bound=Union[Callable[..., Any], type]) KeyType = Union[type, None] logger = logging.getLogger(__name__) def multidispatch(*argtypes: KeyType) -> Callable[[T], FunctionDispatcher[T]]: """Declare function as multidispatch. This decorator takes ``argtypes`` argument types and replace decorated function with :class:`.FunctionDispatcher` object, which is responsible for multiple dispatch feature. """ def _replace_with_dispatcher(func: T) -> FunctionDispatcher[T]: nonlocal argtypes argspec = inspect.getfullargspec(func) if not argtypes: arity = _arity(argspec) if isinstance(func, type): # It's a class we deal with: arity -= 1 argtypes = (object,) * arity dispatcher = cast( FunctionDispatcher[T], functools.update_wrapper(FunctionDispatcher(argspec, len(argtypes)), func), ) dispatcher.register_rule(func, *argtypes) return dispatcher return _replace_with_dispatcher class FunctionDispatcher(Generic[T]): """Multidispatcher for functions. This object dispatch calls to function by its argument types. Usually it is produced by :func:`.multidispatch` decorator. You should not manually create objects of this type. """ registry: Registry[T] def __init__(self, argspec: inspect.FullArgSpec, params_arity: int) -> None: """Initialize dispatcher with ``argspec`` of type :class:`inspect.ArgSpec` and ``params_arity`` that represent number params.""" # Check if we have enough positional arguments for number of type params if _arity(argspec) < params_arity: raise TypeError( "Not enough positional arguments " "for number of type parameters provided." ) self.argspec = argspec self.params_arity = params_arity axis = [(f"arg_{n:d}", TypeAxis()) for n in range(params_arity)] self.registry = Registry(*axis) def check_rule(self, rule: T, *argtypes: KeyType) -> None: """Check if the argument types match wrt number of arguments. Raise TypeError in case of failure. """ # Check if we have the right number of parametrized types if len(argtypes) != self.params_arity: raise TypeError( f"Wrong number of type parameters: have {len(argtypes)}, expected {self.params_arity}." ) # Check if we have the same argspec (by number of args) rule_argspec = inspect.getfullargspec(rule) left_spec = tuple(x and len(x) or 0 for x in rule_argspec[:4]) right_spec = tuple(x and len(x) or 0 for x in self.argspec[:4]) if left_spec != right_spec: raise TypeError( f"Rule does not conform to previous implementations: {left_spec} != {right_spec}." ) def register_rule(self, rule: T, *argtypes: KeyType) -> None: """Register new ``rule`` for ``argtypes``.""" self.check_rule(rule, *argtypes) self.registry.register(rule, *argtypes) def register(self, *argtypes: KeyType) -> Callable[[T], T]: """Decorator for registering new case for multidispatch. New case will be registered for types identified by ``argtypes``. The length of ``argtypes`` should be equal to the length of ``argtypes`` argument were passed corresponding :func:`.multidispatch` call, which also indicated the number of arguments multidispatch dispatches on. """ def register_rule(func: T) -> T: """Register rule wrapper function.""" self.register_rule(func, *argtypes) return func return register_rule def __call__(self, *args: Any, **kwargs: Any) -> Any: """Dispatch call to appropriate rule.""" trimmed_args = args[: self.params_arity] rule = self.registry.lookup(*trimmed_args) if not rule: logger.debug(self.registry._tree) raise TypeError(f"No available rule found for {trimmed_args!r}") return rule(*args, **kwargs) def _arity(argspec: inspect.FullArgSpec) -> int: """Determinal positional arity of argspec.""" args = argspec.args or [] defaults: tuple[Any, ...] | list = argspec.defaults or [] return len(args) - len(defaults) generic-orig-1.1.7/tests/0000775000175000017500000000000015153546432014214 5ustar hasanhasangeneric-orig-1.1.7/tests/test_event.py0000664000175000017500000001017215153546432016747 0ustar hasanhasan"""Tests for :module:`generic.event`.""" from __future__ import annotations from typing import Callable from generic.event import Manager def make_handler(effect: object) -> Callable[[Event], None]: return lambda e: e.effects.append(effect) def create_manager(): return Manager() def test_subscribe_single_event(): events = create_manager() events.subscribe(make_handler("handler1"), EventA) e = EventA() events.handle(e) assert len(e.effects) == 1 assert "handler1" in e.effects def test_subscribe_via_decorator(): events = create_manager() events.subscriber(EventA)(make_handler("handler1")) e = EventA() events.handle(e) assert len(e.effects) == 1 assert "handler1" in e.effects def test_subscribe_event_inheritance(): events = create_manager() events.subscribe(make_handler("handler1"), EventA) events.subscribe(make_handler("handler2"), EventB) ea = EventA() events.handle(ea) assert len(ea.effects) == 1 assert "handler1" in ea.effects eb = EventB() events.handle(eb) assert len(eb.effects) == 2 assert "handler1" in eb.effects assert "handler2" in eb.effects def test_subscribe_event_multiple_inheritance(): events = create_manager() events.subscribe(make_handler("handler1"), EventA) events.subscribe(make_handler("handler2"), EventC) events.subscribe(make_handler("handler3"), EventD) ea = EventA() events.handle(ea) assert len(ea.effects) == 1 assert "handler1" in ea.effects ec = EventC() events.handle(ec) assert len(ec.effects) == 1 assert "handler2" in ec.effects ed = EventD() events.handle(ed) assert len(ed.effects) == 3 assert "handler1" in ed.effects assert "handler2" in ed.effects assert "handler3" in ed.effects def test_subscribe_no_events(): events = create_manager() ea = EventA() events.handle(ea) assert len(ea.effects) == 0 def test_subscribe_base_event(): events = create_manager() events.subscribe(make_handler("handler1"), EventA) ea = EventB() events.handle(ea) assert len(ea.effects) == 1 assert "handler1" in ea.effects def test_subscribe_event_malformed_multiple_inheritance(): events = create_manager() events.subscribe(make_handler("handler1"), EventA) events.subscribe(make_handler("handler2"), EventD) events.subscribe(make_handler("handler3"), EventE) ea = EventA() events.handle(ea) assert len(ea.effects) == 1 assert "handler1" in ea.effects ed = EventD() events.handle(ed) assert len(ed.effects) == 2 assert "handler1" in ed.effects assert "handler2" in ed.effects ee = EventE() events.handle(ee) assert len(ee.effects) == 3 assert "handler1" in ee.effects assert "handler2" in ee.effects assert "handler3" in ee.effects def test_subscribe_event_with_no_subscribers_in_the_middle_of_mro(): events = create_manager() events.subscribe(make_handler("handler1"), Event) events.subscribe(make_handler("handler2"), EventB) eb = EventB() events.handle(eb) assert len(eb.effects) == 2 assert "handler1" in eb.effects assert "handler2" in eb.effects def test_unsubscribe_single_event(): events = create_manager() handler = make_handler("handler1") events.subscribe(handler, EventA) events.unsubscribe(handler, EventA) e = EventA() events.handle(e) assert len(e.effects) == 0 def test_unsubscribe_event_inheritance(): events = create_manager() handler1 = make_handler("handler1") handler2 = make_handler("handler2") events.subscribe(handler1, EventA) events.subscribe(handler2, EventB) events.unsubscribe(handler1, EventA) ea = EventA() events.handle(ea) assert len(ea.effects) == 0 eb = EventB() events.handle(eb) assert len(eb.effects) == 1 assert "handler2" in eb.effects class Event: def __init__(self) -> None: self.effects: list[object] = [] class EventA(Event): pass class EventB(EventA): pass class EventC(Event): pass class EventD(EventA, EventC): pass class EventE(EventD, EventA): pass generic-orig-1.1.7/tests/test_registry.py0000664000175000017500000000773115153546432017505 0ustar hasanhasan"""Tests for :module:`generic.registry`.""" from typing import Union import pytest from generic.registry import Registry, SimpleAxis, TypeAxis class DummyA: pass class DummyB(DummyA): pass def test_one_axis_no_specificity(): registry: Registry[object] = Registry(("foo", SimpleAxis())) a = object() b = object() registry.register(a) registry.register(b, "foo") assert registry.lookup() == a assert registry.lookup("foo") == b assert registry.lookup("bar") is None def test_subtyping_on_axes(): registry: Registry[str] = Registry(("type", TypeAxis())) target1 = "one" registry.register(target1, object) target2 = "two" registry.register(target2, DummyA) target3 = "three" registry.register(target3, DummyB) assert registry.lookup(object()) == target1 assert registry.lookup(DummyA()) == target2 assert registry.lookup(DummyB()) == target3 def test_query_subtyping_on_axes(): registry: Registry[str] = Registry(("type", TypeAxis())) target1 = "one" registry.register(target1, object) target2 = "two" registry.register(target2, DummyA) target3 = "three" registry.register(target3, DummyB) target4 = "four" registry.register(target4, int) assert list(registry.query(object())) == [target1] assert list(registry.query(DummyA())) == [target2, target1] assert list(registry.query(DummyB())) == [target3, target2, target1] assert list(registry.query(3)) == [target4, target1] def test_two_axes(): registry: Registry[Union[str, object]] = Registry( ("type", TypeAxis()), ("name", SimpleAxis()) ) target1 = "one" registry.register(target1, object) target2 = "two" registry.register(target2, DummyA) target3 = "three" registry.register(target3, DummyA, "foo") context1 = object() assert registry.lookup(context1) == target1 context2 = DummyB() assert registry.lookup(context2) == target2 assert registry.lookup(context2, "foo") == target3 target4 = object() registry.register(target4, DummyB) assert registry.lookup(context2) == target4 assert registry.lookup(context2, "foo") == target3 def test_get_registration(): registry: Registry[str] = Registry(("type", TypeAxis()), ("name", SimpleAxis())) registry.register("one", object) registry.register("two", DummyA, "foo") assert registry.get_registration(object) == "one" assert registry.get_registration(DummyA, "foo") == "two" assert registry.get_registration(object, "foo") is None assert registry.get_registration(DummyA) is None def test_register_too_many_keys(): registry: Registry[type] = Registry(("name", SimpleAxis())) with pytest.raises(ValueError): registry.register(object, "one", "two") def test_lookup_too_many_keys(): registry: Registry[object] = Registry(("name", SimpleAxis())) with pytest.raises(ValueError): registry.register(registry.lookup("one", "two")) def test_conflict_error(): registry: Registry[Union[object, type]] = Registry(("name", SimpleAxis())) registry.register(object(), name="foo") with pytest.raises(ValueError): registry.register(object, "foo") def test_skip_nodes(): registry: Registry[str] = Registry( ("one", SimpleAxis()), ("two", SimpleAxis()), ("three", SimpleAxis()) ) registry.register("foo", one=1, three=3) assert registry.lookup(1, three=3) == "foo" def test_miss(): registry: Registry[str] = Registry( ("one", SimpleAxis()), ("two", SimpleAxis()), ("three", SimpleAxis()) ) registry.register("foo", 1, 2) assert registry.lookup(one=1, three=3) is None def test_bad_lookup(): registry: Registry[int] = Registry(("name", SimpleAxis()), ("grade", SimpleAxis())) with pytest.raises(ValueError): registry.register(1, foo=1) with pytest.raises(ValueError): registry.lookup(foo=1) with pytest.raises(ValueError): registry.register(1, "foo", name="foo") generic-orig-1.1.7/tests/test_event_exception.py0000664000175000017500000000243315153546432021026 0ustar hasanhasanfrom __future__ import annotations from sys import version_info from typing import Callable import pytest if version_info < (3, 11): from exceptiongroup import ExceptionGroup from generic.event import Event, Manager @pytest.fixture def events(): return Manager() def make_handler(effect: object) -> Callable[[Event], None]: def handler(e): e.effects.append(effect) raise ValueError(effect) return handler def test_handle_all_subscribers(events): events.subscribe(make_handler("handler1"), MyEvent) events.subscribe(make_handler("handler2"), MyEvent) e = MyEvent() with pytest.raises(ExceptionGroup): events.handle(e) assert len(e.effects) == 2 assert "handler1" in e.effects assert "handler2" in e.effects def test_collect_all_exceptions(events): events.subscribe(make_handler("handler1"), MyEvent) events.subscribe(make_handler("handler2"), MyEvent) e = MyEvent() with pytest.raises(ExceptionGroup) as excinfo: events.handle(e) exc = excinfo.value nested_exc = [str(e) for e in exc.exceptions] assert len(exc.exceptions) == 2 assert "handler1" in nested_exc assert "handler2" in nested_exc class MyEvent: def __init__(self) -> None: self.effects: list[object] = [] generic-orig-1.1.7/tests/test_multidispatch.py0000664000175000017500000001524515153546432020506 0ustar hasanhasan"""Tests for :module:`generic.multidispatch`.""" import logging from inspect import FullArgSpec import pytest from generic.multidispatch import FunctionDispatcher, multidispatch def create_dispatcher( params_arity, args=None, varargs=None, keywords=None, defaults=None ) -> FunctionDispatcher: return FunctionDispatcher( FullArgSpec( args=args, varargs=varargs, varkw=keywords, defaults=defaults, kwonlyargs=[], kwonlydefaults={}, annotations={}, ), params_arity, ) def test_one_argument(): dispatcher = create_dispatcher(1, args=["x"]) dispatcher.register_rule(lambda x: x + 1, int) assert dispatcher(1) == 2 with pytest.raises(TypeError): dispatcher("s") dispatcher.register_rule(lambda x: f"{x}1", str) assert dispatcher(1) == 2 assert dispatcher("1") == "11" with pytest.raises(TypeError): dispatcher(()) def test_two_arguments(): dispatcher = create_dispatcher(2, args=["x", "y"]) dispatcher.register_rule(lambda x, y: x + y + 1, int, int) assert dispatcher(1, 2) == 4 with pytest.raises(TypeError): dispatcher("s", "ss") with pytest.raises(TypeError): dispatcher(1, "ss") with pytest.raises(TypeError): dispatcher("s", 2) dispatcher.register_rule(lambda x, y: x + y + "1", str, str) assert dispatcher(1, 2) == 4 assert dispatcher("1", "2") == "121" with pytest.raises(TypeError): dispatcher("1", 1) with pytest.raises(TypeError): dispatcher(1, "1") dispatcher.register_rule(lambda x, y: str(x) + y + "1", int, str) assert dispatcher(1, 2) == 4 assert dispatcher("1", "2") == "121" assert dispatcher(1, "2") == "121" with pytest.raises(TypeError): dispatcher("1", 1) def test_bottom_rule(): dispatcher = create_dispatcher(1, args=["x"]) dispatcher.register_rule(lambda x: x, object) assert dispatcher(1) == 1 assert dispatcher("1") == "1" assert dispatcher([1]) == [1] assert dispatcher((1,)) == (1,) def test_subtype_evaluation(): class Super: pass class Sub(Super): pass dispatcher = create_dispatcher(1, args=["x"]) dispatcher.register_rule(lambda x: x, Super) o_super = Super() assert dispatcher(o_super) == o_super o_sub = Sub() assert dispatcher(o_sub) == o_sub with pytest.raises(TypeError): dispatcher(object()) dispatcher.register_rule(lambda x: (x, x), Sub) o_super = Super() assert dispatcher(o_super) == o_super o_sub = Sub() assert dispatcher(o_sub) == (o_sub, o_sub) def test_subtype_and_none_evaluation(): class Super: pass class Sub(Super): pass dispatcher = create_dispatcher(2, args=["x", "y"]) dispatcher.register_rule(lambda x, y: (x, y), Super, None) dispatcher.register_rule(lambda x, y: x == y, Sub, Sub) o_super = Super() assert dispatcher(o_super, None) == (o_super, None) o_sub = Sub() assert dispatcher(o_sub, None) == (o_sub, None) with pytest.raises(TypeError): dispatcher(object()) def test_register_rule_with_wrong_arity(): dispatcher = create_dispatcher(1, args=["x"]) dispatcher.register_rule(lambda x: x, int) with pytest.raises(TypeError): dispatcher.register_rule(lambda x, y: x, str) def test_register_rule_with_different_arg_names(): dispatcher = create_dispatcher(1, args=["x"]) dispatcher.register_rule(lambda y: y, int) assert dispatcher(1) == 1 def test_dispatching_with_varargs(): dispatcher = create_dispatcher(1, args=["x"], varargs="va") dispatcher.register_rule(lambda x, *va: x, int) assert dispatcher(1) == 1 with pytest.raises(TypeError): dispatcher("1", 2, 3) def test_dispatching_with_varkw(): dispatcher = create_dispatcher(1, args=["x"], keywords="vk") dispatcher.register_rule(lambda x, **vk: x, int) assert dispatcher(1) == 1 with pytest.raises(TypeError): dispatcher("1", a=1, b=2) def test_dispatching_with_kw(): dispatcher = create_dispatcher(1, args=["x", "y"], defaults=["vk"]) dispatcher.register_rule(lambda x, y=1: x, int) assert dispatcher(1) == 1 with pytest.raises(TypeError): dispatcher("1", k=1) def test_create_dispatcher_with_pos_args_less_multi_arity(): with pytest.raises(TypeError): create_dispatcher(2, args=["x"]) with pytest.raises(TypeError): create_dispatcher(2, args=["x", "y"], defaults=["x"]) def test_register_rule_with_wrong_number_types_parameters(): dispatcher = create_dispatcher(1, args=["x", "y"]) with pytest.raises(TypeError): dispatcher.register_rule(lambda x, y: x, int, str) def test_register_rule_with_partial_dispatching(): dispatcher = create_dispatcher(1, args=["x", "y"]) dispatcher.register_rule(lambda x, y: x, int) assert dispatcher(1, 2) == 1 assert dispatcher(1, "2") == 1 with pytest.raises(TypeError): dispatcher("2", 1) dispatcher.register_rule(lambda x, y: x, str) assert dispatcher(1, 2) == 1 assert dispatcher(1, "2") == 1 assert dispatcher("1", "2") == "1" assert dispatcher("1", 2) == "1" def test_default_dispatcher(): @multidispatch(int, str) def func(x, y): return str(x) + y assert func(1, "2") == "12" with pytest.raises(TypeError): func(1, 2) with pytest.raises(TypeError): func("1", 2) with pytest.raises(TypeError): func("1", "2") def test_multiple_functions(): @multidispatch(int, str) def func(x, y): return str(x) + y @func.register(str, str) def _(x, y): return x + y assert func(1, "2") == "12" assert func("1", "2") == "12" with pytest.raises(TypeError): func(1, 2) with pytest.raises(TypeError): func("1", 2) def test_default(): @multidispatch() def func(x, y): return x + y @func.register(str, str) def _(x, y): return y + x assert func(1, 1) == 2 assert func("1", "2") == "21" def test_on_classes(): @multidispatch() class A: def __init__(self, a, b): self.v = a + b @A.register(str, str) # type: ignore[attr-defined] class B: def __init__(self, a, b): self.v = b + a assert A(1, 1).v == 2 assert A("1", "2").v == "21" def test_logging(caplog): @multidispatch(str, str) def func(x, y): return x + y caplog.set_level(logging.DEBUG) with pytest.raises(TypeError): func(1, 2) rec = caplog.records[0] assert rec.levelname == "DEBUG" assert rec.module == "multidispatch" assert rec.name == "generic.multidispatch" generic-orig-1.1.7/tests/test_multimethod.py0000664000175000017500000000635215153546432020166 0ustar hasanhasanimport pytest from generic.multimethod import has_multimethods, multimethod def test_multimethod(): @has_multimethods class Dummy: @multimethod(int) def foo(self, x): return x + 1 @foo.register(str) # type: ignore[no-redef] def foo(self, x): return f"{x}1" assert Dummy().foo(1) == 2 assert Dummy().foo("1") == "11" with pytest.raises(TypeError): Dummy().foo([]) def test_multimethod_with_two_arguments(): @has_multimethods class Dummy: @multimethod(int, int) def foo(self, x, y): return x * y @foo.register(str, int) # type: ignore[no-redef] def foo(self, s, x): return s * x assert Dummy().foo(1, 1) == 1 assert Dummy().foo("1", 2) == "11" with pytest.raises(TypeError): Dummy().foo([]) def test_multimethod_otherwise_clause(): @has_multimethods class Dummy: @multimethod(int) def foo(self, x): return x + 1 @foo.otherwise # type: ignore[no-redef] def foo(self, x): return type(x) assert Dummy().foo(1) == 2 assert Dummy().foo("") is str assert Dummy().foo([]) is list def test_multimethod_otherwise_clausewith_two_arguments(): @has_multimethods class Dummy: @multimethod(int, int) def foo(self, x, y): return x * y @foo.otherwise # type: ignore[no-redef] def foo(self, s, x): return f"{s} {x}" assert Dummy().foo(1, 2) == 2 assert Dummy().foo("a", []) == "a []" def test_inheritance(): @has_multimethods class Dummy: @multimethod(int) def foo(self, x): return x + 1 @foo.register(float) # type: ignore[no-redef] def foo(self, x): return x + 1.5 @has_multimethods class DummySub(Dummy): @Dummy.foo.register(str) def foo(self, x): return f"{x}1" @foo.register(tuple) # type: ignore[no-redef] def foo(self, x): return x + (1,) @Dummy.foo.register(bool) # type: ignore[no-redef] def foo(self, x): return not x assert Dummy().foo(1) == 2 assert Dummy().foo(1.5) == 3.0 with pytest.raises(TypeError): Dummy().foo("1") assert DummySub().foo(1) == 2 assert DummySub().foo(1.5) == 3.0 assert DummySub().foo("1") == "11" assert DummySub().foo((1, 2)) == (1, 2, 1) assert DummySub().foo(True) is False with pytest.raises(TypeError): DummySub().foo([]) def test_override_in_same_class_not_allowed(): with pytest.raises(ValueError): @has_multimethods class Dummy: @multimethod(str, str) def foo(self, x, y): return x + y @foo.register(str, str) # type: ignore[no-redef] def foo(self, x, y): return y + x def test_inheritance_override(): @has_multimethods class Dummy: @multimethod(int) def foo(self, x): return x + 1 @has_multimethods class DummySub(Dummy): @Dummy.foo.register(int) def foo(self, x): return x + 3 assert Dummy().foo(1) == 2 assert DummySub().foo(1) == 4 generic-orig-1.1.7/poetry.lock0000664000175000017500000022702515153546432015256 0ustar hasanhasan# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "accessible-pygments" version = "0.0.5" description = "A collection of accessible pygments styles" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7"}, {file = "accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872"}, ] [package.dependencies] pygments = ">=1.5" [package.extras] dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] tests = ["hypothesis", "pytest"] [[package]] name = "alabaster" version = "0.7.16" description = "A light, configurable Sphinx theme" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] [[package]] name = "babel" version = "2.18.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" groups = ["docs"] files = [ {file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"}, {file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"}, ] [package.extras] dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] [[package]] name = "beautifulsoup4" version = "4.14.3" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" groups = ["docs"] files = [ {file = "beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb"}, {file = "beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86"}, ] [package.dependencies] soupsieve = ">=1.6.1" typing-extensions = ">=4.0.0" [package.extras] cchardet = ["cchardet"] chardet = ["chardet"] charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] [[package]] name = "certifi" version = "2026.2.25" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["docs"] files = [ {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, ] [[package]] name = "charset-normalizer" version = "3.4.5" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["docs"] files = [ {file = "charset_normalizer-3.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4167a621a9a1a986c73777dbc15d4b5eac8ac5c10393374109a343d4013ec765"}, {file = "charset_normalizer-3.4.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f64c6bf8f32f9133b668c7f7a7cbdbc453412bc95ecdbd157f3b1e377a92990"}, {file = "charset_normalizer-3.4.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:568e3c34b58422075a1b49575a6abc616d9751b4d61b23f712e12ebb78fe47b2"}, {file = "charset_normalizer-3.4.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:036c079aa08a6a592b82487f97c60b439428320ed1b2ea0b3912e99d30c77765"}, {file = "charset_normalizer-3.4.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340810d34ef83af92148e96e3e44cb2d3f910d2bf95e5618a5c467d9f102231d"}, {file = "charset_normalizer-3.4.5-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:cd2d0f0ec9aa977a27731a3209ebbcacebebaf41f902bd453a928bfd281cf7f8"}, {file = "charset_normalizer-3.4.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b362bcd27819f9c07cbf23db4e0e8cd4b44c5ecd900c2ff907b2b92274a7412"}, {file = "charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:77be992288f720306ab4108fe5c74797de327f3248368dfc7e1a916d6ed9e5a2"}, {file = "charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:8b78d8a609a4b82c273257ee9d631ded7fac0d875bdcdccc109f3ee8328cfcb1"}, {file = "charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ba20bdf69bd127f66d0174d6f2a93e69045e0b4036dc1ca78e091bcc765830c4"}, {file = "charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:76a9d0de4d0eab387822e7b35d8f89367dd237c72e82ab42b9f7bf5e15ada00f"}, {file = "charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8fff79bf5978c693c9b1a4d71e4a94fddfb5fe744eb062a318e15f4a2f63a550"}, {file = "charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c7e84e0c0005e3bdc1a9211cd4e62c78ba80bc37b2365ef4410cd2007a9047f2"}, {file = "charset_normalizer-3.4.5-cp310-cp310-win32.whl", hash = "sha256:58ad8270cfa5d4bef1bc85bd387217e14ff154d6630e976c6f56f9a040757475"}, {file = "charset_normalizer-3.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:02a9d1b01c1e12c27883b0c9349e0bcd9ae92e727ff1a277207e1a262b1cbf05"}, {file = "charset_normalizer-3.4.5-cp310-cp310-win_arm64.whl", hash = "sha256:039215608ac7b358c4da0191d10fc76868567fbf276d54c14721bdedeb6de064"}, {file = "charset_normalizer-3.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:610f72c0ee565dfb8ae1241b666119582fdbfe7c0975c175be719f940e110694"}, {file = "charset_normalizer-3.4.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60d68e820af339df4ae8358c7a2e7596badeb61e544438e489035f9fbf3246a5"}, {file = "charset_normalizer-3.4.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b473fc8dca1c3ad8559985794815f06ca3fc71942c969129070f2c3cdf7281"}, {file = "charset_normalizer-3.4.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d4eb8ac7469b2a5d64b5b8c04f84d8bf3ad340f4514b98523805cbf46e3b3923"}, {file = "charset_normalizer-3.4.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bcb3227c3d9aaf73eaaab1db7ccd80a8995c509ee9941e2aae060ca6e4e5d81"}, {file = "charset_normalizer-3.4.5-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:75ee9c1cce2911581a70a3c0919d8bccf5b1cbc9b0e5171400ec736b4b569497"}, {file = "charset_normalizer-3.4.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d1401945cb77787dbd3af2446ff2d75912327c4c3a1526ab7955ecf8600687c"}, {file = "charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a45e504f5e1be0bd385935a8e1507c442349ca36f511a47057a71c9d1d6ea9e"}, {file = "charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e09f671a54ce70b79a1fc1dc6da3072b7ef7251fadb894ed92d9aa8218465a5f"}, {file = "charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d01de5e768328646e6a3fa9e562706f8f6641708c115c62588aef2b941a4f88e"}, {file = "charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:131716d6786ad5e3dc542f5cc6f397ba3339dc0fb87f87ac30e550e8987756af"}, {file = "charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a374cc0b88aa710e8865dc1bd6edb3743c59f27830f0293ab101e4cf3ce9f85"}, {file = "charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d31f0d1671e1534e395f9eb84a68e0fb670e1edb1fe819a9d7f564ae3bc4e53f"}, {file = "charset_normalizer-3.4.5-cp311-cp311-win32.whl", hash = "sha256:cace89841c0599d736d3d74a27bc5821288bb47c5441923277afc6059d7fbcb4"}, {file = "charset_normalizer-3.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:f8102ae93c0bc863b1d41ea0f4499c20a83229f52ed870850892df555187154a"}, {file = "charset_normalizer-3.4.5-cp311-cp311-win_arm64.whl", hash = "sha256:ed98364e1c262cf5f9363c3eca8c2df37024f52a8fa1180a3610014f26eac51c"}, {file = "charset_normalizer-3.4.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed97c282ee4f994ef814042423a529df9497e3c666dca19be1d4cd1129dc7ade"}, {file = "charset_normalizer-3.4.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0294916d6ccf2d069727d65973c3a1ca477d68708db25fd758dd28b0827cff54"}, {file = "charset_normalizer-3.4.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc57a0baa3eeedd99fafaef7511b5a6ef4581494e8168ee086031744e2679467"}, {file = "charset_normalizer-3.4.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ed1a9a204f317ef879b32f9af507d47e49cd5e7f8e8d5d96358c98373314fc60"}, {file = "charset_normalizer-3.4.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad83b8f9379176c841f8865884f3514d905bcd2a9a3b210eaa446e7d2223e4d"}, {file = "charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:a118e2e0b5ae6b0120d5efa5f866e58f2bb826067a646431da4d6a2bdae7950e"}, {file = "charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:754f96058e61a5e22e91483f823e07df16416ce76afa4ebf306f8e1d1296d43f"}, {file = "charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0c300cefd9b0970381a46394902cd18eaf2aa00163f999590ace991989dcd0fc"}, {file = "charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c108f8619e504140569ee7de3f97d234f0fbae338a7f9f360455071ef9855a95"}, {file = "charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d1028de43596a315e2720a9849ee79007ab742c06ad8b45a50db8cdb7ed4a82a"}, {file = "charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:19092dde50335accf365cce21998a1c6dd8eafd42c7b226eb54b2747cdce2fac"}, {file = "charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4354e401eb6dab9aed3c7b4030514328a6c748d05e1c3e19175008ca7de84fb1"}, {file = "charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a68766a3c58fde7f9aaa22b3786276f62ab2f594efb02d0a1421b6282e852e98"}, {file = "charset_normalizer-3.4.5-cp312-cp312-win32.whl", hash = "sha256:1827734a5b308b65ac54e86a618de66f935a4f63a8a462ff1e19a6788d6c2262"}, {file = "charset_normalizer-3.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:728c6a963dfab66ef865f49286e45239384249672cd598576765acc2a640a636"}, {file = "charset_normalizer-3.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:75dfd1afe0b1647449e852f4fb428195a7ed0588947218f7ba929f6538487f02"}, {file = "charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac59c15e3f1465f722607800c68713f9fbc2f672b9eb649fe831da4019ae9b23"}, {file = "charset_normalizer-3.4.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:165c7b21d19365464e8f70e5ce5e12524c58b48c78c1f5a57524603c1ab003f8"}, {file = "charset_normalizer-3.4.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:28269983f25a4da0425743d0d257a2d6921ea7d9b83599d4039486ec5b9f911d"}, {file = "charset_normalizer-3.4.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d27ce22ec453564770d29d03a9506d449efbb9fa13c00842262b2f6801c48cce"}, {file = "charset_normalizer-3.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0625665e4ebdddb553ab185de5db7054393af8879fb0c87bd5690d14379d6819"}, {file = "charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:c23eb3263356d94858655b3e63f85ac5d50970c6e8febcdde7830209139cc37d"}, {file = "charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e6302ca4ae283deb0af68d2fbf467474b8b6aedcd3dab4db187e07f94c109763"}, {file = "charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e51ae7d81c825761d941962450f50d041db028b7278e7b08930b4541b3e45cb9"}, {file = "charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:597d10dec876923e5c59e48dbd366e852eacb2b806029491d307daea6b917d7c"}, {file = "charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5cffde4032a197bd3b42fd0b9509ec60fb70918d6970e4cc773f20fc9180ca67"}, {file = "charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2da4eedcb6338e2321e831a0165759c0c620e37f8cd044a263ff67493be8ffb3"}, {file = "charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:65a126fb4b070d05340a84fc709dd9e7c75d9b063b610ece8a60197a291d0adf"}, {file = "charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7a80a9242963416bd81f99349d5f3fce1843c303bd404f204918b6d75a75fd6"}, {file = "charset_normalizer-3.4.5-cp313-cp313-win32.whl", hash = "sha256:f1d725b754e967e648046f00c4facc42d414840f5ccc670c5670f59f83693e4f"}, {file = "charset_normalizer-3.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:e37bd100d2c5d3ba35db9c7c5ba5a9228cbcffe5c4778dc824b164e5257813d7"}, {file = "charset_normalizer-3.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:93b3b2cc5cf1b8743660ce77a4f45f3f6d1172068207c1defc779a36eea6bb36"}, {file = "charset_normalizer-3.4.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8197abe5ca1ffb7d91e78360f915eef5addff270f8a71c1fc5be24a56f3e4873"}, {file = "charset_normalizer-3.4.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2aecdb364b8a1802afdc7f9327d55dad5366bc97d8502d0f5854e50712dbc5f"}, {file = "charset_normalizer-3.4.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a66aa5022bf81ab4b1bebfb009db4fd68e0c6d4307a1ce5ef6a26e5878dfc9e4"}, {file = "charset_normalizer-3.4.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d77f97e515688bd615c1d1f795d540f32542d514242067adcb8ef532504cb9ee"}, {file = "charset_normalizer-3.4.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01a1ed54b953303ca7e310fafe0fe347aab348bd81834a0bcd602eb538f89d66"}, {file = "charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:b2d37d78297b39a9eb9eb92c0f6df98c706467282055419df141389b23f93362"}, {file = "charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e71bbb595973622b817c042bd943c3f3667e9c9983ce3d205f973f486fec98a7"}, {file = "charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cd966c2559f501c6fd69294d082c2934c8dd4719deb32c22961a5ac6db0df1d"}, {file = "charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d5e52d127045d6ae01a1e821acfad2f3a1866c54d0e837828538fabe8d9d1bd6"}, {file = "charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:30a2b1a48478c3428d047ed9690d57c23038dac838a87ad624c85c0a78ebeb39"}, {file = "charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d8ed79b8f6372ca4254955005830fd61c1ccdd8c0fac6603e2c145c61dd95db6"}, {file = "charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c5af897b45fa606b12464ccbe0014bbf8c09191e0a66aab6aa9d5cf6e77e0c94"}, {file = "charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1088345bcc93c58d8d8f3d783eca4a6e7a7752bbff26c3eee7e73c597c191c2e"}, {file = "charset_normalizer-3.4.5-cp314-cp314-win32.whl", hash = "sha256:ee57b926940ba00bca7ba7041e665cc956e55ef482f851b9b65acb20d867e7a2"}, {file = "charset_normalizer-3.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:4481e6da1830c8a1cc0b746b47f603b653dadb690bcd851d039ffaefe70533aa"}, {file = "charset_normalizer-3.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:97ab7787092eb9b50fb47fa04f24c75b768a606af1bcba1957f07f128a7219e4"}, {file = "charset_normalizer-3.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e22d1059b951e7ae7c20ef6b06afd10fb95e3c41bf3c4fbc874dba113321c193"}, {file = "charset_normalizer-3.4.5-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afca7f78067dd27c2b848f1b234623d26b87529296c6c5652168cc1954f2f3b2"}, {file = "charset_normalizer-3.4.5-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec56a2266f32bc06ed3c3e2a8f58417ce02f7e0356edc89786e52db13c593c98"}, {file = "charset_normalizer-3.4.5-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b970382e4a36bed897c19f310f31d7d13489c11b4f468ddfba42d41cddfb918"}, {file = "charset_normalizer-3.4.5-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:573ef5814c4b7c0d59a7710aa920eaaaef383bd71626aa420fba27b5cab92e8d"}, {file = "charset_normalizer-3.4.5-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:50bcbca6603c06a1dcc7b056ed45c37715fb5d2768feb3bcd37d2313c587a5b9"}, {file = "charset_normalizer-3.4.5-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1f2da5cbb9becfcd607757a169e38fb82aa5fd86fae6653dea716e7b613fe2cf"}, {file = "charset_normalizer-3.4.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc1c64934b8faf7584924143eb9db4770bbdb16659626e1a1a4d9efbcb68d947"}, {file = "charset_normalizer-3.4.5-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:ae8b03427410731469c4033934cf473426faff3e04b69d2dfb64a4281a3719f8"}, {file = "charset_normalizer-3.4.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:b3e71afc578b98512bfe7bdb822dd6bc57d4b0093b4b6e5487c1e96ad4ace242"}, {file = "charset_normalizer-3.4.5-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:4b8551b6e6531e156db71193771c93bda78ffc4d1e6372517fe58ad3b91e4659"}, {file = "charset_normalizer-3.4.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:65b3c403a5b6b8034b655e7385de4f72b7b244869a22b32d4030b99a60593eca"}, {file = "charset_normalizer-3.4.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8ce11cd4d62d11166f2b441e30ace226c19a3899a7cf0796f668fba49a9fb123"}, {file = "charset_normalizer-3.4.5-cp38-cp38-win32.whl", hash = "sha256:66dee73039277eb35380d1b82cccc69cc82b13a66f9f4a18da32d573acf02b7c"}, {file = "charset_normalizer-3.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:d29dd9c016f2078b43d0c357511e87eee5b05108f3dd603423cb389b89813969"}, {file = "charset_normalizer-3.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:259cd1ca995ad525f638e131dbcc2353a586564c038fc548a3fe450a91882139"}, {file = "charset_normalizer-3.4.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a28afb04baa55abf26df544e3e5c6534245d3daa5178bc4a8eeb48202060d0e"}, {file = "charset_normalizer-3.4.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ff95a9283de8a457e6b12989de3f9f5193430f375d64297d323a615ea52cbdb3"}, {file = "charset_normalizer-3.4.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:708c7acde173eedd4bfa4028484426ba689d2103b28588c513b9db2cd5ecde9c"}, {file = "charset_normalizer-3.4.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa92ec1102eaff840ccd1021478af176a831f1bccb08e526ce844b7ddda85c22"}, {file = "charset_normalizer-3.4.5-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:5fea359734b140d0d6741189fea5478c6091b54ffc69d7ce119e0a05637d8c99"}, {file = "charset_normalizer-3.4.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e545b51da9f9af5c67815ca0eb40676c0f016d0b0381c86f20451e35696c5f95"}, {file = "charset_normalizer-3.4.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:30987f4a8ed169983f93e1be8ffeea5214a779e27ed0b059835c7afe96550ad7"}, {file = "charset_normalizer-3.4.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:149ec69866c3d6c2fb6f758dbc014ecb09f30b35a5ca90b6a8a2d4e54e18fdfe"}, {file = "charset_normalizer-3.4.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:530beedcec9b6e027e7a4b6ce26eed36678aa39e17da85e6e03d7bd9e8e9d7c9"}, {file = "charset_normalizer-3.4.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:14498a429321de554b140013142abe7608f9d8ccc04d7baf2ad60498374aefa2"}, {file = "charset_normalizer-3.4.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2820a98460c83663dd8ec015d9ddfd1e4879f12e06bb7d0500f044fb477d2770"}, {file = "charset_normalizer-3.4.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:aa2f963b4da26daf46231d9b9e0e2c9408a751f8f0d0f44d2de56d3caf51d294"}, {file = "charset_normalizer-3.4.5-cp39-cp39-win32.whl", hash = "sha256:82cc7c2ad42faec8b574351f8bc2a0c049043893853317bd9bb309f5aba6cb5a"}, {file = "charset_normalizer-3.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:92263f7eca2f4af326cd20de8d16728d2602f7cfea02e790dcde9d83c365d7cc"}, {file = "charset_normalizer-3.4.5-cp39-cp39-win_arm64.whl", hash = "sha256:014837af6fabf57121b6254fa8ade10dceabc3528b27b721a64bbc7b8b1d4eb4"}, {file = "charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0"}, {file = "charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev", "docs"] markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.13.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ {file = "coverage-7.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415"}, {file = "coverage-7.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a32ebc02a1805adf637fc8dec324b5cdacd2e493515424f70ee33799573d661b"}, {file = "coverage-7.13.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e24f9156097ff9dc286f2f913df3a7f63c0e333dcafa3c196f2c18b4175ca09a"}, {file = "coverage-7.13.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8041b6c5bfdc03257666e9881d33b1abc88daccaf73f7b6340fb7946655cd10f"}, {file = "coverage-7.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a09cfa6a5862bc2fc6ca7c3def5b2926194a56b8ab78ffcf617d28911123012"}, {file = "coverage-7.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:296f8b0af861d3970c2a4d8c91d48eb4dd4771bcef9baedec6a9b515d7de3def"}, {file = "coverage-7.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e101609bcbbfb04605ea1027b10dc3735c094d12d40826a60f897b98b1c30256"}, {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa3feb8db2e87ff5e6d00d7e1480ae241876286691265657b500886c98f38bda"}, {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4fc7fa81bbaf5a02801b65346c8b3e657f1d93763e58c0abdf7c992addd81a92"}, {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:33901f604424145c6e9c2398684b92e176c0b12df77d52db81c20abd48c3794c"}, {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:bb28c0f2cf2782508a40cec377935829d5fcc3ad9a3681375af4e84eb34b6b58"}, {file = "coverage-7.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d107aff57a83222ddbd8d9ee705ede2af2cc926608b57abed8ef96b50b7e8f9"}, {file = "coverage-7.13.4-cp310-cp310-win32.whl", hash = "sha256:a6f94a7d00eb18f1b6d403c91a88fd58cfc92d4b16080dfdb774afc8294469bf"}, {file = "coverage-7.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:2cb0f1e000ebc419632bbe04366a8990b6e32c4e0b51543a6484ffe15eaeda95"}, {file = "coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053"}, {file = "coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11"}, {file = "coverage-7.13.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3998e5a32e62fdf410c0dbd3115df86297995d6e3429af80b8798aad894ca7aa"}, {file = "coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7"}, {file = "coverage-7.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3aa4e7b9e416774b21797365b358a6e827ffadaaca81b69ee02946852449f00"}, {file = "coverage-7.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71ca20079dd8f27fcf808817e281e90220475cd75115162218d0e27549f95fef"}, {file = "coverage-7.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e2f25215f1a359ab17320b47bcdaca3e6e6356652e8256f2441e4ef972052903"}, {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d65b2d373032411e86960604dc4edac91fdfb5dca539461cf2cbe78327d1e64f"}, {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94eb63f9b363180aff17de3e7c8760c3ba94664ea2695c52f10111244d16a299"}, {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e856bf6616714c3a9fbc270ab54103f4e685ba236fa98c054e8f87f266c93505"}, {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:65dfcbe305c3dfe658492df2d85259e0d79ead4177f9ae724b6fb245198f55d6"}, {file = "coverage-7.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b507778ae8a4c915436ed5c2e05b4a6cecfa70f734e19c22a005152a11c7b6a9"}, {file = "coverage-7.13.4-cp311-cp311-win32.whl", hash = "sha256:784fc3cf8be001197b652d51d3fd259b1e2262888693a4636e18879f613a62a9"}, {file = "coverage-7.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f"}, {file = "coverage-7.13.4-cp311-cp311-win_arm64.whl", hash = "sha256:79e73a76b854d9c6088fe5d8b2ebe745f8681c55f7397c3c0a016192d681045f"}, {file = "coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459"}, {file = "coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3"}, {file = "coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634"}, {file = "coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3"}, {file = "coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa"}, {file = "coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3"}, {file = "coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a"}, {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7"}, {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc"}, {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47"}, {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985"}, {file = "coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0"}, {file = "coverage-7.13.4-cp312-cp312-win32.whl", hash = "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246"}, {file = "coverage-7.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126"}, {file = "coverage-7.13.4-cp312-cp312-win_arm64.whl", hash = "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d"}, {file = "coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9"}, {file = "coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac"}, {file = "coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea"}, {file = "coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b"}, {file = "coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525"}, {file = "coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242"}, {file = "coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148"}, {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a"}, {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23"}, {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80"}, {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea"}, {file = "coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a"}, {file = "coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d"}, {file = "coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd"}, {file = "coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af"}, {file = "coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d"}, {file = "coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12"}, {file = "coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b"}, {file = "coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9"}, {file = "coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092"}, {file = "coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9"}, {file = "coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26"}, {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2"}, {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940"}, {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c"}, {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0"}, {file = "coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b"}, {file = "coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9"}, {file = "coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd"}, {file = "coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997"}, {file = "coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601"}, {file = "coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689"}, {file = "coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c"}, {file = "coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129"}, {file = "coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552"}, {file = "coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a"}, {file = "coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356"}, {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71"}, {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5"}, {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98"}, {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5"}, {file = "coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0"}, {file = "coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb"}, {file = "coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505"}, {file = "coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2"}, {file = "coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056"}, {file = "coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc"}, {file = "coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9"}, {file = "coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf"}, {file = "coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55"}, {file = "coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72"}, {file = "coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a"}, {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6"}, {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3"}, {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750"}, {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39"}, {file = "coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0"}, {file = "coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea"}, {file = "coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932"}, {file = "coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b"}, {file = "coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0"}, {file = "coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "docutils" version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] [[package]] name = "exceptiongroup" version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["main", "dev"] markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] [package.dependencies] typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} [package.extras] test = ["pytest (>=6)"] [[package]] name = "furo" version = "2025.12.19" description = "A clean customisable Sphinx documentation theme." optional = false python-versions = ">=3.8" groups = ["docs"] files = [ {file = "furo-2025.12.19-py3-none-any.whl", hash = "sha256:bb0ead5309f9500130665a26bee87693c41ce4dbdff864dbfb6b0dae4673d24f"}, {file = "furo-2025.12.19.tar.gz", hash = "sha256:188d1f942037d8b37cd3985b955839fea62baa1730087dc29d157677c857e2a7"}, ] [package.dependencies] accessible-pygments = ">=0.0.5" beautifulsoup4 = "*" pygments = ">=2.7" sphinx = ">=7.0,<10.0" sphinx-basic-ng = ">=1.0.0b2" [[package]] name = "idna" version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" groups = ["docs"] files = [ {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, ] [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "imagesize" version = "1.5.0" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" groups = ["docs"] files = [ {file = "imagesize-1.5.0-py2.py3-none-any.whl", hash = "sha256:32677681b3f434c2cb496f00e89c5a291247b35b1f527589909e008057da5899"}, {file = "imagesize-1.5.0.tar.gz", hash = "sha256:8bfc5363a7f2133a89f0098451e0bcb1cd71aba4dc02bbcecb39d99d40e1b94f"}, ] [[package]] name = "iniconfig" version = "2.3.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] [[package]] name = "jinja2" version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" groups = ["docs"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "markupsafe" version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] [[package]] name = "packaging" version = "26.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["dev", "docs"] files = [ {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, ] [[package]] name = "pluggy" version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "pygments" version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" groups = ["dev", "docs"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" version = "8.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, ] [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} iniconfig = ">=1" packaging = ">=20" pluggy = ">=1.5,<2" pygments = ">=2.7.2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" version = "7.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, ] [package.dependencies] coverage = {version = ">=7.10.6", extras = ["toml"]} pluggy = ">=1.2" pytest = ">=7" [package.extras] testing = ["process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "requests" version = "2.32.5" description = "Python HTTP for Humans." optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] certifi = ">=2017.4.17" charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "snowballstemmer" version = "3.0.1" description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" groups = ["docs"] files = [ {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, ] [[package]] name = "soupsieve" version = "2.8.3" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95"}, {file = "soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349"}, ] [[package]] name = "sphinx" version = "7.4.7" description = "Python documentation generator" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, ] [package.dependencies] alabaster = ">=0.7.14,<0.8.0" babel = ">=2.13" colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} docutils = ">=0.20,<0.22" imagesize = ">=1.3" Jinja2 = ">=3.1" packaging = ">=23.0" Pygments = ">=2.17" requests = ">=2.30.0" snowballstemmer = ">=2.2" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.9" tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["flake8 (>=6.0)", "importlib-metadata (>=6.0)", "mypy (==1.10.1)", "pytest (>=6.0)", "ruff (==0.5.2)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-docutils (==0.21.0.20240711)", "types-requests (>=2.30.0)"] test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] [[package]] name = "sphinx-basic-ng" version = "1.0.0b2" description = "A modern skeleton for Sphinx themes." optional = false python-versions = ">=3.7" groups = ["docs"] files = [ {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, ] [package.dependencies] sphinx = ">=4.0" [package.extras] docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, ] [package.extras] lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, ] [package.extras] lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, ] [package.extras] lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" groups = ["docs"] files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] [package.extras] test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, ] [package.extras] lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["defusedxml (>=0.7.1)", "pytest"] [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, ] [package.extras] lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "tomli" version = "2.4.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev", "docs"] files = [ {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"}, {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"}, {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"}, {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"}, {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"}, {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"}, {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"}, {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"}, {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"}, {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"}, {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"}, {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"}, {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"}, {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"}, {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"}, {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"}, {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"}, {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"}, {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"}, {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"}, {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"}, {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"}, {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"}, {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"}, {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"}, {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"}, {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"}, {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"}, {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"}, {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"}, {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"}, {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"}, {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"}, {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"}, {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"}, {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"}, {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"}, {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"}, {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"}, {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"}, {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"}, {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"}, {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"}, {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, ] markers = {dev = "python_full_version <= \"3.11.0a6\"", docs = "python_version == \"3.10\""} [[package]] name = "typing-extensions" version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["main", "dev", "docs"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] markers = {main = "python_version == \"3.10\"", dev = "python_version == \"3.10\""} [[package]] name = "urllib3" version = "2.6.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["docs"] files = [ {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, ] [package.extras] brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [metadata] lock-version = "2.1" python-versions = ">=3.10" content-hash = "30f4058da5fb79922069b92729e48290805df09d107b85429d86c1e87fdcdf10" generic-orig-1.1.7/LICENSES/0000775000175000017500000000000015153546432014257 5ustar hasanhasangeneric-orig-1.1.7/LICENSES/BSD-3-Clause.txt0000664000175000017500000000304715153546432017006 0ustar hasanhasanCopyright (c) 2009-2010 Christopher Michael Rossi. Copyright (c) 2010 Andrey Popp. Copyright (c) 2019 Arjan Molenaar and Dan Yeaw. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the copyright holder 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 HOLDER 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. generic-orig-1.1.7/.gitignore0000664000175000017500000000025015153546432015037 0ustar hasanhasandist/ generic.egg-info/ docs/_build/ .coverage .ropeproject __pycache__ .mypy_cache cc-test-reporter htmlcov/ .python-version # Virtualenv .venv # Tox .tox .vscode/ generic-orig-1.1.7/pyproject.toml0000664000175000017500000000412615153546432015771 0ustar hasanhasan[project] name = "generic" version = "1.1.7" description = "Generic programming library for Python" authors = [ { name = "Andrey Popp", email = "8mayday@gmail.com" }, { name = "Arjan Molenaar", email = "gaphor@gmail.com" }, ] maintainers = [ { name = "Arjan Molenaar", email = "gaphor@gmail.com" }, { name = "Dan Yeaw", email = "dan@yeaw.me" }, ] license = "BSD-3-Clause" license-files = [ "LICENSES/BSD-3-Clause.txt" ] readme = "README.md" keywords = ["generic", "multi dispatch", "dispatch", "event"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Programming Language :: Python", ] requires-python = ">=3.10" dependencies = [ "exceptiongroup>=1.0.0 ; python_version < '3.11'", ] [project.urls] homepage = "https://generic.readthedocs.io/" repository = "https://github.com/gaphor/generic" documentation = "https://generic.readthedocs.io/" [tool.poetry] requires-poetry = ">=2.0" [tool.poetry.group.dev.dependencies] pytest = "^8.3" pytest-cov = "^7.0" [tool.poetry.group.docs] optional=true [tool.poetry.group.docs.dependencies] sphinx = ">=4.3,<8.0" furo = ">=2022,<2026" [tool.pytest.ini_options] testpaths = [ "tests", "docs", ] addopts = [ "--doctest-modules", "--doctest-glob='*.rst'", "--import-mode=importlib", ] [tool.coverage.run] source = ["generic"] [tool.mypy] python_version = 3.10 warn_return_any = true warn_unused_configs = true warn_redundant_casts = true check_untyped_defs = true strict_optional = true show_error_codes = true ignore_missing_imports=true warn_unused_ignores = true namespace_packages = true [[tool.mypy.overrides]] module = [ "pytest.*", "conf", ] ignore_missing_imports = true warn_unreachable = true [tool.ruff] exclude = [ ".venv", "dist", ] line-length = 88 [tool.ruff.lint] ignore = ["E501"] select = [ "B", "B9", "C", "E", "F", "W", ] [tool.ruff.mccabe] max-complexity = 18 [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" generic-orig-1.1.7/.reuse/0000775000175000017500000000000015153546432014253 5ustar hasanhasangeneric-orig-1.1.7/.reuse/dep50000664000175000017500000000047415153546432015040 0ustar hasanhasanFormat: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Generic Upstream-Contact: Generic Library contributors Source: https://github.com/gaphor/generic Files: * Copyright: 2009-2010 Christopher Michael Rossi, 2010 Andrey Popp, 2019 Arjan Molenaar & Dan Yeaw License: BSD-3-Clause generic-orig-1.1.7/README.md0000664000175000017500000000606415153546432014337 0ustar hasanhasan# Generic programming library for Python [![Build state](https://github.com/gaphor/generic/workflows/build/badge.svg)](https://github.com/gaphor/generic/actions) [![Maintainability](https://qlty.sh/gh/gaphor/projects/generic/maintainability.svg)](https://qlty.sh/gh/gaphor/projects/generic) [![Code Coverage](https://qlty.sh/gh/gaphor/projects/generic/coverage.svg)](https://qlty.sh/gh/gaphor/projects/generic) [![Documentation Status](https://readthedocs.org/projects/generic/badge/?version=latest)](https://generic.readthedocs.io/en/latest/?badge=latest) [![Matrix](https://img.shields.io/badge/chat-on%20Matrix-success)](https://app.element.io/#/room/#gaphor_Lobby:gitter.im) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/gaphor/generic/badge)](https://securityscorecards.dev/viewer/?platform=github.com&org=gaphor&repo=generic) Generic is a library for [Generic programming](https://en.wikipedia.org/wiki/Generic_programming), also known as [Multiple dispatch](https://en.wikipedia.org/wiki/Multiple_dispatch). The Generic library supports: * multi-dispatch: like `functools.singledispatch`, but for more than one parameter * multi-methods: multi-dispatch, but for methods * event dispatching: based on a hierarchical event structure (event objects) You can read [documentation](http://generic.readthedocs.org/en/latest/index.html) hosted at excellent readthedocs.org project. Development takes place on [github](http://github.com/gaphor/generic). # Changes ## 1.1.6 - Refactoring ## 1.1.5 - Fix regression with super type dispatching - Dependency updates ## 1.1.4 - Dependency updates ## 1.1.3 - Dependency updates ## 1.1.2 - Replace print statements with logging - Enable trusted publisher for PyPI - Create Security Policy - Update LICENSE to BSD 3-Clause - Add support for Python 3.12 - Simplify build: drop tox - Update documentation theme to Furo - Switch linting to ruff ## 1.1.1 - Add support for Python 3.11 - Move mypy configuration to pyproject.toml - Enable automatic release of new versions with CI ## 1.1.0 - Rename `master` branch to `main` - `generic.event.Manager` executes all handlers and throws an `ExceptionGroup` in case of errors ## 1.0.1 - Add Support for Python 3.10, Drop Support for Python 3.7 - Enable Pre-commit Hooks for isort, toml, yaml, pyupgrade, docformatter, and flake8 - Migrate to GitHub Actions ## 1.0.0 - Updated documentation on [Readthedocs](https://generic.readthedocs.io) - Fix `multimethod.otherwise` clause ## 1.0.0b1 - Ported the code to Python 3.7, Python 2 is no longer supported - Multimethods now have their own module - The interface now mimics `functools.singledispatch`: - the `when` method has been renamed to `register` - overriding of methods is no longer possible ## 0.3.1 - Minor fixes in distribution. ## 0.3 - Event management with event inheritance support. ## 0.2 - Methods with multidispatch by object type and positional arguments. - Override multifunctions with ``override`` method. ## 0.1 - Registry with simple and type axes. - Functions with multidispatch by positional arguments.