From d61a69d7361f6a201136779c1d62afb27803b2e2 Mon Sep 17 00:00:00 2001 From: LeeeeT Date: Thu, 29 Dec 2022 21:20:01 +0300 Subject: [PATCH] feat: add network types --- .devcontainer/devcontainer.json | 27 + .../workflows/build-dev-container-image.yaml | 23 + .github/workflows/ci.yaml | 195 +--- .github/workflows/publish-release.yaml | 16 +- .pre-commit-config.yaml | 53 +- .vscode/settings.json | 7 + docs/usage/constrained_types/float.md | 2 +- docs/usage/constrained_types/str.md | 6 +- poetry.lock | 952 +++++++----------- pyproject.toml | 34 +- tests/condition/test_equals.py | 9 + tests/condition/test_re_pattern_alias.py | 14 + tests/condition/test_regex_pattern_alias.py | 14 + .../test_numeric.py => test_comparison.py} | 2 +- tests/error/parsing/test_numeric.py | 5 - tests/error/parsing/{type => }/test_sized.py | 2 +- tests/error/parsing/test_str.py | 21 + tests/error/parsing/type/test_str.py | 7 - .../parse_json/test_dict_to_dataclass.py | 12 +- .../test_float_to_subclass_of_float.py | 4 +- .../parse_json/test_int_to_subclass_of_int.py | 2 +- .../test_list_to_subclass_of_list.py | 2 +- .../parse_json/test_str_to_re_pattern.py | 17 + .../parse_json/test_str_to_regex_pattern.py | 16 + .../parse_json/test_str_to_subclass_of_str.py | 2 +- tests/type/float/test_exclusive_maximum.py | 14 +- tests/type/float/test_exclusive_minimum.py | 14 +- tests/type/float/test_maximum.py | 16 +- tests/type/float/test_minimum.py | 12 +- tests/type/float/test_negative.py | 12 +- tests/type/float/test_non_negative.py | 10 +- tests/type/float/test_non_positive.py | 10 +- tests/type/float/test_portion.py | 14 +- tests/type/float/test_positive.py | 12 +- tests/type/frozenset/test_maximum_length.py | 2 +- tests/type/frozenset/test_minimum_length.py | 2 +- tests/type/frozenset/test_non_empty.py | 2 +- tests/type/int/test_maximum.py | 2 +- tests/type/int/test_minimum.py | 2 +- tests/type/int/test_negative.py | 2 +- tests/type/int/test_non_negative.py | 2 +- tests/type/int/test_non_positive.py | 2 +- tests/type/int/test_positive.py | 2 +- tests/type/list/test_maximum_length.py | 2 +- tests/type/list/test_minimum_length.py | 2 +- tests/type/list/test_non_empty.py | 2 +- tests/type/set/test_maximum_length.py | 2 +- tests/type/set/test_minimum_length.py | 2 +- tests/type/set/test_non_empty.py | 2 +- tests/type/sized/test_maximum_length.py | 2 +- tests/type/sized/test_minimum_length.py | 2 +- tests/type/sized/test_non_empty.py | 2 +- tests/type/str/test_maximum_length.py | 2 +- tests/type/str/test_minimum_length.py | 2 +- .../{test_pattern.py => test_re_pattern.py} | 10 +- tests/type/str/test_regex_pattern.py | 22 + tests/type/tuple/test_maximum_length.py | 2 +- tests/type/tuple/test_minimum_length.py | 2 +- tests/type/tuple/test_non_empty.py | 2 +- valtypes/collection.py | 6 +- valtypes/condition.py | 26 + .../{type/numeric.py => comparison.py} | 29 +- valtypes/error/parsing/dataclass.py | 6 +- valtypes/error/parsing/float.py | 23 + valtypes/error/parsing/int.py | 23 + valtypes/error/parsing/literal.py | 2 +- valtypes/error/parsing/numeric.py | 17 - valtypes/error/parsing/sequence.py | 2 +- valtypes/error/parsing/{type => }/sized.py | 0 valtypes/error/parsing/str.py | 46 + valtypes/error/parsing/type/__init__.py | 3 - valtypes/error/parsing/type/generic.py | 7 - valtypes/error/parsing/type/str.py | 19 - valtypes/forward_ref.py | 2 +- valtypes/parsing/__init__.py | 3 +- valtypes/parsing/factory/__init__.py | 4 +- valtypes/parsing/factory/{abc.py => base.py} | 0 valtypes/parsing/factory/composite.py | 12 +- valtypes/parsing/factory/const.py | 19 + valtypes/parsing/factory/dict_to_dataclass.py | 2 +- valtypes/parsing/factory/from_callable.py | 2 +- valtypes/parsing/factory/from_json.py | 40 +- valtypes/parsing/factory/iterable_to_list.py | 2 +- valtypes/parsing/factory/mapping_to_dict.py | 2 +- .../parsing/factory/object_to_dataclass.py | 2 +- valtypes/parsing/factory/object_to_list.py | 2 +- valtypes/parsing/factory/shortcut.py | 2 +- valtypes/parsing/factory/to_init_var.py | 2 +- valtypes/parsing/factory/to_literal.py | 2 +- valtypes/parsing/factory/to_subclass_of.py | 2 +- .../parsing/factory/to_subclass_of_generic.py | 2 +- valtypes/parsing/factory/to_union.py | 2 +- valtypes/parsing/parser/__init__.py | 24 +- valtypes/parsing/parser/{abc.py => base.py} | 0 valtypes/parsing/parser/dict_to_dataclass.py | 4 +- valtypes/parsing/parser/from_callable.py | 2 +- valtypes/parsing/parser/iterable_to_list.py | 2 +- valtypes/parsing/parser/mapping_to_dict.py | 2 +- valtypes/parsing/parser/match_to_ipv4.py | 14 + valtypes/parsing/parser/match_to_ipv6.py | 86 ++ valtypes/parsing/parser/misc.py | 28 + valtypes/parsing/parser/object_to_type.py | 2 +- valtypes/parsing/parser/str_to_ipv4.py | 11 + valtypes/parsing/parser/str_to_re_match.py | 19 + valtypes/parsing/parser/str_to_re_pattern.py | 16 + valtypes/parsing/parser/str_to_regex_match.py | 20 + .../parsing/parser/str_to_regex_pattern.py | 16 + valtypes/parsing/parser/to_literal.py | 2 +- valtypes/parsing/parser/to_literal_choice.py | 2 +- .../parser/to_literal_choice_preparse.py | 2 +- valtypes/parsing/parser/to_union.py | 2 +- valtypes/parsing/{rule/base.py => rule.py} | 15 +- valtypes/parsing/rule/__init__.py | 4 - valtypes/parsing/rule/abc.py | 20 - .../type => valtypes/pattern}/__init__.py | 0 valtypes/pattern/network.py | 43 + valtypes/regex/__init__.py | 0 valtypes/regex/network.py | 99 ++ valtypes/type/float.py | 8 +- valtypes/type/frozenset.py | 7 +- valtypes/type/int.py | 12 +- valtypes/type/list.py | 10 +- valtypes/type/network.py | 251 +++++ valtypes/type/set.py | 14 +- valtypes/type/sized.py | 2 +- valtypes/type/str.py | 23 +- valtypes/type/tuple.py | 7 +- valtypes/typing.py | 17 + 128 files changed, 1628 insertions(+), 1107 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/workflows/build-dev-container-image.yaml create mode 100644 .vscode/settings.json create mode 100644 tests/condition/test_equals.py create mode 100644 tests/condition/test_re_pattern_alias.py create mode 100644 tests/condition/test_regex_pattern_alias.py rename tests/error/parsing/{type/test_numeric.py => test_comparison.py} (82%) delete mode 100644 tests/error/parsing/test_numeric.py rename tests/error/parsing/{type => }/test_sized.py (77%) create mode 100644 tests/error/parsing/test_str.py delete mode 100644 tests/error/parsing/type/test_str.py create mode 100644 tests/parsing/parse_json/test_str_to_re_pattern.py create mode 100644 tests/parsing/parse_json/test_str_to_regex_pattern.py rename tests/type/str/{test_pattern.py => test_re_pattern.py} (56%) create mode 100644 tests/type/str/test_regex_pattern.py rename valtypes/error/parsing/{type/numeric.py => comparison.py} (54%) create mode 100644 valtypes/error/parsing/float.py create mode 100644 valtypes/error/parsing/int.py delete mode 100644 valtypes/error/parsing/numeric.py rename valtypes/error/parsing/{type => }/sized.py (100%) create mode 100644 valtypes/error/parsing/str.py delete mode 100644 valtypes/error/parsing/type/__init__.py delete mode 100644 valtypes/error/parsing/type/generic.py delete mode 100644 valtypes/error/parsing/type/str.py rename valtypes/parsing/factory/{abc.py => base.py} (100%) create mode 100644 valtypes/parsing/factory/const.py rename valtypes/parsing/parser/{abc.py => base.py} (100%) create mode 100644 valtypes/parsing/parser/match_to_ipv4.py create mode 100644 valtypes/parsing/parser/match_to_ipv6.py create mode 100644 valtypes/parsing/parser/misc.py create mode 100644 valtypes/parsing/parser/str_to_ipv4.py create mode 100644 valtypes/parsing/parser/str_to_re_match.py create mode 100644 valtypes/parsing/parser/str_to_re_pattern.py create mode 100644 valtypes/parsing/parser/str_to_regex_match.py create mode 100644 valtypes/parsing/parser/str_to_regex_pattern.py rename valtypes/parsing/{rule/base.py => rule.py} (59%) delete mode 100644 valtypes/parsing/rule/__init__.py delete mode 100644 valtypes/parsing/rule/abc.py rename {tests/error/parsing/type => valtypes/pattern}/__init__.py (100%) create mode 100644 valtypes/pattern/network.py create mode 100644 valtypes/regex/__init__.py create mode 100644 valtypes/regex/network.py create mode 100644 valtypes/type/network.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..a587076 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +{ + "name": "Valtypes", + "image": "mcr.microsoft.com/devcontainers/python:0-3.11", + "features": { + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers-contrib/features/zsh-plugins:0": { + "plugins": [ + "zsh-autosuggestions zsh-syntax-highlighting" + ], + "omzPlugins": [ + "https://github.com/zsh-users/zsh-autosuggestions https://github.com/zsh-users/zsh-syntax-highlighting" + ] + }, + "ghcr.io/devcontainers-contrib/features/poetry:2": {} + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.isort", + "bungcip.better-toml", + "ryanluker.vscode-coverage-gutters" + ] + } + }, + "postCreateCommand": "poetry install" +} diff --git a/.github/workflows/build-dev-container-image.yaml b/.github/workflows/build-dev-container-image.yaml new file mode 100644 index 0000000..b9c15d2 --- /dev/null +++ b/.github/workflows/build-dev-container-image.yaml @@ -0,0 +1,23 @@ +name: Build Dev Container image + +on: + push: + branches: [ main ] + +jobs: + build: + name: Build Dev Container image + runs-on: ubuntu-latest + steps: + - name: Login to GHCR + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build + uses: devcontainers/ci@v0.2 + with: + imageName: ghcr.io/${{ github.repository }} + cacheFrom: ghcr.io/${{ github.repository }} + push: always diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f560f69..f5fe344 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,185 +9,50 @@ on: jobs: flake8: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ '3.11' ] steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Restore cache and venv - id: cache - uses: actions/cache@v3 - with: - path: | - ~/.cache - .venv - key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} - - name: Install Poetry - if: steps.cache.outputs.cache-hit != 'true' - uses: snok/install-poetry@v1.3.3 - with: - virtualenvs-in-project: true - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: poetry install --with dev + - uses: actions/checkout@v3 - name: Run flake8 - run: | - source .venv/bin/activate - pre-commit run --all-files flake8 + uses: devcontainers/ci@v0.2 + with: + cacheFrom: ghcr.io/${{ github.repository }} + runCmd: poetry run pre-commit run --all-files flake8 -# mypy: -# runs-on: ubuntu-latest -# strategy: -# matrix: -# python-version: [ '3.11' ] -# steps: -# - name: Checkout -# uses: actions/checkout@v3 -# - name: Setup Python -# uses: actions/setup-python@v4 -# with: -# python-version: ${{ matrix.python-version }} -# - name: Restore cache and venv -# id: cache -# uses: actions/cache@v3 -# with: -# path: | -# ~/.cache -# .venv -# key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} -# - name: Restore mypy cache -# uses: actions/cache@v3 -# with: -# path: .mypy_cache -# key: ${{ matrix.python-version }} -# - name: Install Poetry -# if: steps.cache.outputs.cache-hit != 'true' -# uses: snok/install-poetry@v1.3.3 -# with: -# virtualenvs-in-project: true -# - name: Install dependencies -# if: steps.cache.outputs.cache-hit != 'true' -# run: poetry install --with dev -# - name: Run mypy -# run: | -# source .venv/bin/activate -# pre-commit run --all-files mypy + # mypy: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Run mypy + # uses: devcontainers/ci@v0.2 + # with: + # cacheFrom: ghcr.io/${{ github.repository }} + # runCmd: poetry run pre-commit run --all-files mypy pyright: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ '3.11' ] steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Restore cache and venv - id: cache - uses: actions/cache@v3 - with: - path: | - ~/.cache - .venv - key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} - - name: Install Poetry - if: steps.cache.outputs.cache-hit != 'true' - uses: snok/install-poetry@v1.3.3 - with: - virtualenvs-in-project: true - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: poetry install --with dev + - uses: actions/checkout@v3 - name: Run pyright - run: | - source .venv/bin/activate - pre-commit run --all-files pyright + uses: devcontainers/ci@v0.2 + with: + cacheFrom: ghcr.io/${{ github.repository }} + runCmd: poetry run pre-commit run --all-files pyright pyright-verify: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ '3.11' ] steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Restore cache and venv - id: cache - uses: actions/cache@v3 - with: - path: | - ~/.cache - .venv - key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} - - name: Install Poetry - if: steps.cache.outputs.cache-hit != 'true' - uses: snok/install-poetry@v1.3.3 - with: - virtualenvs-in-project: true - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: poetry install --with dev + - uses: actions/checkout@v3 - name: Run pyright-verify - run: | - source .venv/bin/activate - pre-commit run --all-files pyright-verify + uses: devcontainers/ci@v0.2 + with: + cacheFrom: ghcr.io/${{ github.repository }} + runCmd: poetry run pre-commit run --all-files pyright-verify pytest: - runs-on: ${{ matrix.os }} - defaults: - run: - shell: bash - strategy: - matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - python-version: [ '3.11' ] + runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Restore cache and venv - id: cache - uses: actions/cache@v3 - with: - path: | - ~/.cache - .venv - key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }} - - name: Restore pytest cache - uses: actions/cache@v3 - with: - path: .pytest_cache - key: pytest-${{ runner.os }}-${{ matrix.python-version }} - - name: Install Poetry - if: steps.cache.outputs.cache-hit != 'true' - uses: snok/install-poetry@v1.3.3 + - uses: actions/checkout@v3 + - name: Run pytest + uses: devcontainers/ci@v0.2 with: - virtualenvs-in-project: true - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: poetry install --with dev - - name: Run pytest on Windows - if: runner.os == 'Windows' - run: | - source .venv/scripts/activate - pre-commit run --all-files pytest - - name: Run pytest on normal OS - if: runner.os != 'Windows' - run: | - source .venv/bin/activate - pre-commit run --all-files pytest + cacheFrom: ghcr.io/${{ github.repository }} + runCmd: poetry run pre-commit run --all-files pytest diff --git a/.github/workflows/publish-release.yaml b/.github/workflows/publish-release.yaml index ea04249..59acba7 100644 --- a/.github/workflows/publish-release.yaml +++ b/.github/workflows/publish-release.yaml @@ -9,18 +9,12 @@ jobs: name: Publish release runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - name: Install Poetry - uses: snok/install-poetry@v1.3.3 - with: - virtualenvs-create: false + - uses: actions/checkout@v3 - name: Build the package - run: poetry build + uses: devcontainers/ci@v0.2 + with: + cacheFrom: ghcr.io/${{ github.repository }} + runCmd: poetry build - name: Publish the package to PyPI uses: pypa/gh-action-pypi-publish@master with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8f5a5dd..81810e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,62 +1,57 @@ -ci: - autofix_commit_msg: 'refactor: auto fixes from pre-commit hooks' - autoupdate_commit_msg: 'build(deps): update pre-commit hooks' - skip: [ pyproject-flake8, pyright, pyright-verify, pytest ] - repos: - - repo: https://github.com/pycqa/isort - rev: 5.11.4 + - repo: local hooks: - id: isort + name: isort + entry: poetry run isort + language: system + pass_filenames: false stages: [ commit ] - - repo: https://github.com/aio-libs/sort-all - rev: v1.2.0 - hooks: - id: sort-all name: sort-all + entry: poetry run sort-all + language: system stages: [ commit ] - - repo: https://github.com/psf/black - rev: 22.12.0 - hooks: - id: black + name: black + entry: poetry run black . + language: system + pass_filenames: false stages: [ commit ] - - repo: https://github.com/csachs/pyproject-flake8 - rev: v6.0.0.post1 - hooks: - - id: pyproject-flake8 + - id: flake8 name: flake8 - alias: flake8 + entry: poetry run pflake8 valtypes tests testing + language: system + pass_filenames: false stages: [ commit ] - - repo: local - hooks: -# - id: mypy -# name: mypy -# entry: bash -c "pip install poetry && poetry install --with mypy && mypy" -# language: system -# pass_filenames: false -# stages: [ commit ] + # - id: mypy + # name: mypy + # entry: poetry run mypy + # language: system + # pass_filenames: false + # stages: [ commit ] - id: pyright name: pyright - entry: bash -c "pip install poetry && poetry install --with pyright && pyright" + entry: poetry run pyright language: system pass_filenames: false stages: [ commit ] - id: pyright-verify name: pyright-verify - entry: bash -c "pip install poetry && poetry install --with pyright-verify && pyright --verifytypes valtypes" + entry: poetry run pyright --verifytypes valtypes language: system pass_filenames: false stages: [ commit ] - id: pytest name: pytest - entry: bash -c "pip install poetry && poetry install --with pytest && pytest" + entry: poetry run pytest language: system pass_filenames: false stages: [ commit ] diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4b7b7ea --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "terminal.integrated.defaultProfile.linux": "zsh", + "python.testing.pytestEnabled": true, + "python.formatting.provider": "black", + "python.analysis.typeCheckingMode": "strict", + "python.linting.enabled": false, +} diff --git a/docs/usage/constrained_types/float.md b/docs/usage/constrained_types/float.md index ff684b9..e6cd455 100644 --- a/docs/usage/constrained_types/float.md +++ b/docs/usage/constrained_types/float.md @@ -138,6 +138,6 @@ from valtypes.type.float import Portion Portion(0.) # passes Portion(1.) # passes -Portion(-1.) # raises valtypes.error.parsing.type.numeric.Minimum +Portion(-.1) # raises valtypes.error.parsing.type.numeric.Minimum Portion(1.1) # raises valtypes.error.parsing.type.numeric.Maximum ``` diff --git a/docs/usage/constrained_types/str.md b/docs/usage/constrained_types/str.md index e977243..c1f3031 100644 --- a/docs/usage/constrained_types/str.md +++ b/docs/usage/constrained_types/str.md @@ -62,17 +62,17 @@ NonEmpty("foo") # passes NonEmpty() # raises valtypes.error.parsing.type.sized.MinimumLength ``` -## `Pattern` +## `RePattern` Type for representing a `str` that fully matches a given regular expression. The regular expression is stored in the `__pattern__` attribute. ```python import re -from valtypes.type.str import Pattern +from valtypes.type.str import RePattern -class Numeric(Pattern): +class Numeric(RePattern): __pattern__ = re.compile(r"\d+") diff --git a/poetry.lock b/poetry.lock index 4e1f253..d6aaf09 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,24 +1,57 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + [[package]] name = "attrs" -version = "22.1.0" +version = "22.2.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] [package.extras] -dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] -docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] [[package]] -name = "certifi" -version = "2022.12.7" -description = "Python package for providing Mozilla's CA Bundle." +name = "black" +version = "22.12.0" +description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cfgv" @@ -27,17 +60,10 @@ description = "Validate configuration and produce human readable error messages. category = "dev" optional = false python-versions = ">=3.6.1" - -[[package]] -name = "charset-normalizer" -version = "2.1.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" -optional = false -python-versions = ">=3.6.0" - -[package.extras] -unicode-backport = ["unicodedata2"] +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] [[package]] name = "click" @@ -46,6 +72,10 @@ description = "Composable command line interface toolkit" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -57,14 +87,71 @@ description = "Cross-platform colored terminal text." category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +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 = "6.5.0" +version = "7.0.5" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "coverage-7.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b"}, + {file = "coverage-7.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809"}, + {file = "coverage-7.0.5-cp310-cp310-win32.whl", hash = "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21"}, + {file = "coverage-7.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad"}, + {file = "coverage-7.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca"}, + {file = "coverage-7.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095"}, + {file = "coverage-7.0.5-cp311-cp311-win32.whl", hash = "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831"}, + {file = "coverage-7.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea"}, + {file = "coverage-7.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47"}, + {file = "coverage-7.0.5-cp37-cp37m-win32.whl", hash = "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882"}, + {file = "coverage-7.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d"}, + {file = "coverage-7.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb"}, + {file = "coverage-7.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499"}, + {file = "coverage-7.0.5-cp38-cp38-win32.whl", hash = "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16"}, + {file = "coverage-7.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af"}, + {file = "coverage-7.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab"}, + {file = "coverage-7.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1"}, + {file = "coverage-7.0.5-cp39-cp39-win32.whl", hash = "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904"}, + {file = "coverage-7.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f"}, + {file = "coverage-7.0.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0"}, + {file = "coverage-7.0.5.tar.gz", hash = "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45"}, +] [package.extras] toml = ["tomli"] @@ -76,159 +163,140 @@ description = "Distribution utilities" category = "dev" optional = false python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] [[package]] name = "filelock" -version = "3.8.0" +version = "3.9.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, + {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, +] [package.extras] -docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] -testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] [[package]] -name = "ghp-import" -version = "2.1.0" -description = "Copy your docs directly to the gh-pages branch." +name = "flake8" +version = "6.0.0" +description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.8.1" +files = [ + {file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, + {file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, +] [package.dependencies] -python-dateutil = ">=2.8.1" - -[package.extras] -dev = ["flake8", "markdown", "twine", "wheel"] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.10.0,<2.11.0" +pyflakes = ">=3.0.0,<3.1.0" [[package]] name = "identify" -version = "2.5.8" +version = "2.5.13" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "identify-2.5.13-py2.py3-none-any.whl", hash = "sha256:8aa48ce56e38c28b6faa9f261075dea0a942dfbb42b341b4e711896cbb40f3f7"}, + {file = "identify-2.5.13.tar.gz", hash = "sha256:abb546bca6f470228785338a01b539de8a85bbf46491250ae03363956d8ebb10"}, +] [package.extras] license = ["ukkonen"] -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" -optional = false -python-versions = ">=3.5" - [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "dev" optional = false python-versions = ">=3.7" - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] [[package]] -name = "markdown" -version = "3.3.7" -description = "Python implementation of Markdown." +name = "isort" +version = "5.11.4" +description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7.0" +files = [ + {file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"}, + {file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"}, +] [package.extras] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "markupsafe" -version = "2.1.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" -optional = false -python-versions = ">=3.7" +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile-deprecated-finder = ["pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] -name = "mergedeep" -version = "1.3.4" -description = "A deep merge function for 🐍." +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" category = "dev" optional = false python-versions = ">=3.6" - -[[package]] -name = "mkdocs" -version = "1.4.2" -description = "Project documentation with Markdown." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} -ghp-import = ">=1.0" -jinja2 = ">=2.11.1" -markdown = ">=3.2.1,<3.4" -mergedeep = ">=1.3.4" -packaging = ">=20.5" -pyyaml = ">=5.1" -pyyaml-env-tag = ">=0.1" -watchdog = ">=2.0" - -[package.extras] -i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] - -[[package]] -name = "mkdocs-material" -version = "9.0.3" -description = "Documentation that simply works" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -colorama = ">=0.4" -jinja2 = ">=3.0" -markdown = ">=3.2" -mkdocs = ">=1.4.2" -mkdocs-material-extensions = ">=1.1" -pygments = ">=2.14" -pymdown-extensions = ">=9.9" -regex = ">=2022.4.24" -requests = ">=2.26" - -[[package]] -name = "mkdocs-material-extensions" -version = "1.1" -description = "Extension pack for Python Markdown and MkDocs Material." -category = "dev" -optional = false -python-versions = ">=3.7" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] [[package]] name = "mypy" -version = "0.990" +version = "0.991" description = "Optional static typing for Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, + {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, + {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, + {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, + {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, + {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, + {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, + {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, + {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, + {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, + {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, + {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, + {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, + {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, + {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, + {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, + {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, + {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, + {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, + {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, + {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, + {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, + {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, + {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, + {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, + {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, + {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, + {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, + {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, + {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, +] [package.dependencies] mypy-extensions = ">=0.4.3" @@ -247,6 +315,10 @@ description = "Experimental type system extensions for programs checked with the category = "dev" optional = false python-versions = "*" +files = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] [[package]] name = "nodeenv" @@ -255,32 +327,53 @@ description = "Node.js virtual environment builder" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, +] [package.dependencies] setuptools = "*" [[package]] name = "packaging" -version = "21.3" +version = "23.0" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, +] -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +[[package]] +name = "pathspec" +version = "0.10.3" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, + {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, +] [[package]] name = "platformdirs" -version = "2.5.4" +version = "2.6.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, + {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, +] [package.extras] -docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"] -test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -289,6 +382,10 @@ description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] [package.extras] dev = ["pre-commit", "tox"] @@ -296,60 +393,73 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.20.0" +version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" +virtualenv = ">=20.10.0" [[package]] -name = "pygments" -version = "2.14.0" -description = "Pygments is a syntax highlighting package written in Python." +name = "pycodestyle" +version = "2.10.0" +description = "Python style guide checker" category = "dev" optional = false python-versions = ">=3.6" - -[package.extras] -plugins = ["importlib-metadata"] +files = [ + {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, + {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, +] [[package]] -name = "pymdown-extensions" -version = "9.9" -description = "Extension pack for Python Markdown." +name = "pyflakes" +version = "3.0.1" +description = "passive checker of Python programs" category = "dev" optional = false -python-versions = ">=3.7" - -[package.dependencies] -markdown = ">=3.2" +python-versions = ">=3.6" +files = [ + {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, + {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, +] [[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" +name = "pyproject-flake8" +version = "6.0.0.post1" +description = "pyproject-flake8 (`pflake8`), a monkey patching wrapper to connect flake8 with pyproject.toml configuration" category = "dev" optional = false -python-versions = ">=3.6.8" +python-versions = ">=3.8.1" +files = [ + {file = "pyproject-flake8-6.0.0.post1.tar.gz", hash = "sha256:d43421caca0ef8a672874405fe63c722b0333e3c22c41648c6df60f21bab2b6b"}, + {file = "pyproject_flake8-6.0.0.post1-py3-none-any.whl", hash = "sha256:bdc7ca9b967b9724983903489b8943b72c668178fb69f03e8774ec74f6a13782"}, +] -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] +[package.dependencies] +flake8 = "6.0.0" [[package]] name = "pyright" -version = "1.1.279" +version = "1.1.288" description = "Command line wrapper for pyright" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pyright-1.1.288-py3-none-any.whl", hash = "sha256:ab5da004e2de3b0567c685aa8d38bba68d872b1b4a20f1013400ace571a7efc7"}, + {file = "pyright-1.1.288.tar.gz", hash = "sha256:c45594c5833b01d5125bc291d2498d4ed0f2c2e3dd4fd8236fbdaf597099f617"}, +] [package.dependencies] nodeenv = ">=1.6.0" @@ -365,6 +475,10 @@ description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, + {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, +] [package.dependencies] attrs = ">=19.2.0" @@ -383,6 +497,10 @@ description = "Pytest plugin for measuring coverage." category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, + {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, +] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} @@ -391,17 +509,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - [[package]] name = "pyyaml" version = "6.0" @@ -409,379 +516,7 @@ description = "YAML parser and emitter for Python" category = "dev" optional = false python-versions = ">=3.6" - -[[package]] -name = "pyyaml-env-tag" -version = "0.1" -description = "A custom YAML tag for referencing environment variables in YAML files. " -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyyaml = "*" - -[[package]] -name = "regex" -version = "2022.10.31" -description = "Alternative regular expression module, to replace re." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "requests" -version = "2.28.1" -description = "Python HTTP for Humans." -category = "dev" -optional = false -python-versions = ">=3.7, <4" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "setuptools" -version = "65.5.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "typing-extensions" -version = "4.4.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "urllib3" -version = "1.26.12" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "virtualenv" -version = "20.16.7" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.4.1,<4" -platformdirs = ">=2.4,<3" - -[package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "watchdog" -version = "2.1.9" -description = "Filesystem events monitoring" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.11" -content-hash = "594b00fe4421b240edd71e34a626cae67da31244f2292ab6db57fb65c1f47ce9" - -[metadata.files] -attrs = [ - {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, - {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, -] -certifi = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] -cfgv = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, - {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, -] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -colorama = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -coverage = [ - {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, - {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, - {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, - {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, - {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, - {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, - {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, - {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, - {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, - {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, - {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, - {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, - {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, - {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, - {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, - {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, -] -distlib = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] -filelock = [ - {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, - {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, -] -ghp-import = [ - {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, - {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, -] -identify = [ - {file = "identify-2.5.8-py2.py3-none-any.whl", hash = "sha256:48b7925fe122720088aeb7a6c34f17b27e706b72c61070f27fe3789094233440"}, - {file = "identify-2.5.8.tar.gz", hash = "sha256:7a214a10313b9489a0d61467db2856ae8d0b8306fc923e03a9effa53d8aedc58"}, -] -idna = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] -markdown = [ - {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, - {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, -] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -mergedeep = [ - {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, - {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, -] -mkdocs = [ - {file = "mkdocs-1.4.2-py3-none-any.whl", hash = "sha256:c8856a832c1e56702577023cd64cc5f84948280c1c0fcc6af4cd39006ea6aa8c"}, - {file = "mkdocs-1.4.2.tar.gz", hash = "sha256:8947af423a6d0facf41ea1195b8e1e8c85ad94ac95ae307fe11232e0424b11c5"}, -] -mkdocs-material = [ - {file = "mkdocs_material-9.0.3-py3-none-any.whl", hash = "sha256:cedbbf84e156370489907d3c5b79999fcf6563f61a96965ec4c2513d303fa706"}, - {file = "mkdocs_material-9.0.3.tar.gz", hash = "sha256:918fe38f504ca397b388b6c45445c22cb9acab61f00ade78d5f3edf299b6c9df"}, -] -mkdocs-material-extensions = [ - {file = "mkdocs_material_extensions-1.1-py3-none-any.whl", hash = "sha256:bcc2e5fc70c0ec50e59703ee6e639d87c7e664c0c441c014ea84461a90f1e902"}, - {file = "mkdocs_material_extensions-1.1.tar.gz", hash = "sha256:96ca979dae66d65c2099eefe189b49d5ac62f76afb59c38e069ffc7cf3c131ec"}, -] -mypy = [ - {file = "mypy-0.990-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aaf1be63e0207d7d17be942dcf9a6b641745581fe6c64df9a38deb562a7dbafa"}, - {file = "mypy-0.990-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d555aa7f44cecb7ea3c0ac69d58b1a5afb92caa017285a8e9c4efbf0518b61b4"}, - {file = "mypy-0.990-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f694d6d09a460b117dccb6857dda269188e3437c880d7b60fa0014fa872d1e9"}, - {file = "mypy-0.990-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:269f0dfb6463b8780333310ff4b5134425157ef0d2b1d614015adaf6d6a7eabd"}, - {file = "mypy-0.990-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8798c8ed83aa809f053abff08664bdca056038f5a02af3660de00b7290b64c47"}, - {file = "mypy-0.990-cp310-cp310-win_amd64.whl", hash = "sha256:47a9955214615108c3480a500cfda8513a0b1cd3c09a1ed42764ca0dd7b931dd"}, - {file = "mypy-0.990-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4a8a6c10f4c63fbf6ad6c03eba22c9331b3946a4cec97f008e9ffb4d3b31e8e2"}, - {file = "mypy-0.990-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd2dd3730ba894ec2a2082cc703fbf3e95a08479f7be84912e3131fc68809d46"}, - {file = "mypy-0.990-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7da0005e47975287a92b43276e460ac1831af3d23032c34e67d003388a0ce8d0"}, - {file = "mypy-0.990-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:262c543ef24deb10470a3c1c254bb986714e2b6b1a67d66daf836a548a9f316c"}, - {file = "mypy-0.990-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3ff201a0c6d3ea029d73b1648943387d75aa052491365b101f6edd5570d018ea"}, - {file = "mypy-0.990-cp311-cp311-win_amd64.whl", hash = "sha256:1767830da2d1afa4e62b684647af0ff79b401f004d7fa08bc5b0ce2d45bcd5ec"}, - {file = "mypy-0.990-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6826d9c4d85bbf6d68cb279b561de6a4d8d778ca8e9ab2d00ee768ab501a9852"}, - {file = "mypy-0.990-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46897755f944176fbc504178422a5a2875bbf3f7436727374724842c0987b5af"}, - {file = "mypy-0.990-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0680389c34284287fe00e82fc8bccdea9aff318f7e7d55b90d967a13a9606013"}, - {file = "mypy-0.990-cp37-cp37m-win_amd64.whl", hash = "sha256:b08541a06eed35b543ae1a6b301590eb61826a1eb099417676ddc5a42aa151c5"}, - {file = "mypy-0.990-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:be88d665e76b452c26fb2bdc3d54555c01226fba062b004ede780b190a50f9db"}, - {file = "mypy-0.990-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b8f4a8213b1fd4b751e26b59ae0e0c12896568d7e805861035c7a15ed6dc9eb"}, - {file = "mypy-0.990-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b6f85c2ad378e3224e017904a051b26660087b3b76490d533b7344f1546d3ff"}, - {file = "mypy-0.990-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee5f99817ee70254e7eb5cf97c1b11dda29c6893d846c8b07bce449184e9466"}, - {file = "mypy-0.990-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49082382f571c3186ce9ea0bd627cb1345d4da8d44a8377870f4442401f0a706"}, - {file = "mypy-0.990-cp38-cp38-win_amd64.whl", hash = "sha256:aba38e3dd66bdbafbbfe9c6e79637841928ea4c79b32e334099463c17b0d90ef"}, - {file = "mypy-0.990-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9d851c09b981a65d9d283a8ccb5b1d0b698e580493416a10942ef1a04b19fd37"}, - {file = "mypy-0.990-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d847dd23540e2912d9667602271e5ebf25e5788e7da46da5ffd98e7872616e8e"}, - {file = "mypy-0.990-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc6019808580565040cd2a561b593d7c3c646badd7e580e07d875eb1bf35c695"}, - {file = "mypy-0.990-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a3150d409609a775c8cb65dbe305c4edd7fe576c22ea79d77d1454acd9aeda8"}, - {file = "mypy-0.990-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3227f14fe943524f5794679156488f18bf8d34bfecd4623cf76bc55958d229c5"}, - {file = "mypy-0.990-cp39-cp39-win_amd64.whl", hash = "sha256:c76c769c46a1e6062a84837badcb2a7b0cdb153d68601a61f60739c37d41cc74"}, - {file = "mypy-0.990-py3-none-any.whl", hash = "sha256:8f1940325a8ed460ba03d19ab83742260fa9534804c317224e5d4e5aa588e2d6"}, - {file = "mypy-0.990.tar.gz", hash = "sha256:72382cb609142dba3f04140d016c94b4092bc7b4d98ca718740dc989e5271b8d"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -nodeenv = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -platformdirs = [ - {file = "platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"}, - {file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -pre-commit = [ - {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, - {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, -] -pygments = [ - {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, - {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, -] -pymdown-extensions = [ - {file = "pymdown_extensions-9.9-py3-none-any.whl", hash = "sha256:ac698c15265680db5eb13cd4342abfcde2079ac01e5486028f47a1b41547b859"}, - {file = "pymdown_extensions-9.9.tar.gz", hash = "sha256:0f8fb7b74a37a61cc34e90b2c91865458b713ec774894ffad64353a5fce85cfc"}, -] -pyparsing = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] -pyright = [ - {file = "pyright-1.1.279-py3-none-any.whl", hash = "sha256:5eaa6830c0a701dc72586277be98d35762d2c27e01a9c2fbf01ee4e84e785797"}, - {file = "pyright-1.1.279.tar.gz", hash = "sha256:6f3ac7d12e036e0d1a57c1581d03d781e99b42408b8a6f0ef8ae85bcfe58fa57"}, -] -pytest = [ - {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, - {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, -] -pytest-cov = [ - {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, - {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -pyyaml = [ +files = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, @@ -823,11 +558,15 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] -pyyaml-env-tag = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, -] -regex = [ + +[[package]] +name = "regex" +version = "2022.10.31" +description = "Alternative regular expression module, to replace re." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "regex-2022.10.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a8ff454ef0bb061e37df03557afda9d785c905dab15584860f982e88be73015f"}, {file = "regex-2022.10.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1eba476b1b242620c266edf6325b443a2e22b633217a9835a52d8da2b5c051f9"}, {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0e5af9a9effb88535a472e19169e09ce750c3d442fb222254a276d77808620b"}, @@ -917,58 +656,85 @@ regex = [ {file = "regex-2022.10.31-cp39-cp39-win_amd64.whl", hash = "sha256:957403a978e10fb3ca42572a23e6f7badff39aa1ce2f4ade68ee452dc6807692"}, {file = "regex-2022.10.31.tar.gz", hash = "sha256:a3a98921da9a1bf8457aeee6a551948a83601689e5ecdd736894ea9bbec77e83"}, ] -requests = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, -] -setuptools = [ - {file = "setuptools-65.5.1-py3-none-any.whl", hash = "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31"}, - {file = "setuptools-65.5.1.tar.gz", hash = "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f"}, + +[[package]] +name = "setuptools" +version = "65.7.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-65.7.0-py3-none-any.whl", hash = "sha256:8ab4f1dbf2b4a65f7eec5ad0c620e84c34111a68d3349833494b9088212214dd"}, + {file = "setuptools-65.7.0.tar.gz", hash = "sha256:4d3c92fac8f1118bb77a22181355e29c239cabfe2b9effdaa665c66b711136d7"}, ] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "sort-all" +version = "1.2.0" +description = "Automatically Sort __all__ records alphabetically" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sort_all-1.2.0-py3-none-any.whl", hash = "sha256:ccc0fc7191a486ff826cb4d21c2b67d93f9d9cb5eb72e8d572d017d50d4eca88"}, + {file = "sort_all-1.2.0.tar.gz", hash = "sha256:1eb6a91cc61f36bd48d697f687377c6eb67b4ef98e2850fc65130502bae945d8"}, ] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, + +[package.dependencies] +tokenize-rt = ">=3.0.1" + +[[package]] +name = "tokenize-rt" +version = "5.0.0" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenize_rt-5.0.0-py2.py3-none-any.whl", hash = "sha256:c67772c662c6b3dc65edf66808577968fb10badfc2042e3027196bed4daf9e5a"}, + {file = "tokenize_rt-5.0.0.tar.gz", hash = "sha256:3160bc0c3e8491312d0485171dea861fc160a240f5f5766b72a1165408d10740"}, ] -typing-extensions = [ + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] -urllib3 = [ - {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, - {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, -] -virtualenv = [ - {file = "virtualenv-20.16.7-py3-none-any.whl", hash = "sha256:efd66b00386fdb7dbe4822d172303f40cd05e50e01740b19ea42425cbe653e29"}, - {file = "virtualenv-20.16.7.tar.gz", hash = "sha256:8691e3ff9387f743e00f6bb20f70121f5e4f596cae754531f2b3b3a1b1ac696e"}, -] -watchdog = [ - {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"}, - {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d"}, - {file = "watchdog-2.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658"}, - {file = "watchdog-2.1.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591"}, - {file = "watchdog-2.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33"}, - {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846"}, - {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3"}, - {file = "watchdog-2.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654"}, - {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39"}, - {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7"}, - {file = "watchdog-2.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd"}, - {file = "watchdog-2.1.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3"}, - {file = "watchdog-2.1.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d"}, - {file = "watchdog-2.1.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_armv7l.whl", hash = "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_i686.whl", hash = "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64.whl", hash = "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_s390x.whl", hash = "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1"}, - {file = "watchdog-2.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6"}, - {file = "watchdog-2.1.9-py3-none-win32.whl", hash = "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1"}, - {file = "watchdog-2.1.9-py3-none-win_amd64.whl", hash = "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c"}, - {file = "watchdog-2.1.9-py3-none-win_ia64.whl", hash = "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428"}, - {file = "watchdog-2.1.9.tar.gz", hash = "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609"}, + +[[package]] +name = "virtualenv" +version = "20.17.1" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, + {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, ] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.4.1,<4" +platformdirs = ">=2.4,<3" + +[package.extras] +docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "6409f780b2b2fc79f5d1683984b43bd95ca2885fd8cb7148fd5475f85d675c03" diff --git a/pyproject.toml b/pyproject.toml index 2a49035..7b4562e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,30 +23,16 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.11" +regex = "^2022.10.31" [tool.poetry.group.dev.dependencies] -pre-commit = "^2.20.0" - -[tool.poetry.group.mypy] -optional = true -[tool.poetry.group.mypy.dependencies] -mypy = "^0.990" -pytest = "^7.2.0" - -[tool.poetry.group.pyright] -optional = true -[tool.poetry.group.pyright.dependencies] -pyright = "^1.1.279" -pytest = "^7.2.0" - -[tool.poetry.group.pyright-verify] -optional = true -[tool.poetry.group.pyright-verify.dependencies] -pyright = "^1.1.279" - -[tool.poetry.group.pytest] -optional = true -[tool.poetry.group.pytest.dependencies] +pre-commit = "^2.21.0" +isort = "^5.11.4" +sort-all = "^1.2.0" +black = "^22.12.0" +pyproject-flake8 = "^6.0.0.post1" +mypy = "^0.991" +pyright = "^1.1.288" pytest = "^7.2.0" pytest-cov = "^4.0.0" @@ -60,7 +46,7 @@ line-length = 151 [tool.flake8] max-line-length = 151 -ignore = "E203, W503, F402" +ignore = "E203, F402, W503, W504" [tool.mypy] files = ["valtypes", "tests", "testing"] @@ -84,7 +70,7 @@ reportUnnecessaryIsInstance = false [tool.pytest.ini_options] testpaths = ["tests"] -addopts = "--cov=valtypes" +addopts = "--cov=valtypes --cov-report=xml" [tool.coverage.run] branch = true diff --git a/tests/condition/test_equals.py b/tests/condition/test_equals.py new file mode 100644 index 0000000..df5893d --- /dev/null +++ b/tests/condition/test_equals.py @@ -0,0 +1,9 @@ +from valtypes.condition import Equals + + +def test_returns_true_if_values_are_equal() -> None: + assert Equals(True).check(1) + + +def test_returns_false_if_values_are_different() -> None: + assert not Equals(True).check(0) diff --git a/tests/condition/test_re_pattern_alias.py b/tests/condition/test_re_pattern_alias.py new file mode 100644 index 0000000..fb643e2 --- /dev/null +++ b/tests/condition/test_re_pattern_alias.py @@ -0,0 +1,14 @@ +import re + +import regex + +from valtypes.condition import re_pattern_alias + + +def test_returns_true_if_value_is_re_pattern_alias() -> None: + assert re_pattern_alias.check(re.Pattern[str]) + + +def test_returns_true_if_value_is_not_re_pattern_alias() -> None: + assert not re_pattern_alias.check(regex.Pattern[str]) + assert not re_pattern_alias.check(re.Pattern[bytes]) diff --git a/tests/condition/test_regex_pattern_alias.py b/tests/condition/test_regex_pattern_alias.py new file mode 100644 index 0000000..152a651 --- /dev/null +++ b/tests/condition/test_regex_pattern_alias.py @@ -0,0 +1,14 @@ +import re + +import regex + +from valtypes.condition import regex_pattern_alias + + +def test_returns_true_if_value_is_regex_pattern_alias() -> None: + assert regex_pattern_alias.check(regex.Pattern[str]) + + +def test_returns_true_if_value_is_not_regex_pattern_alias() -> None: + assert not regex_pattern_alias.check(re.Pattern[str]) + assert not regex_pattern_alias.check(regex.Pattern[bytes]) diff --git a/tests/error/parsing/type/test_numeric.py b/tests/error/parsing/test_comparison.py similarity index 82% rename from tests/error/parsing/type/test_numeric.py rename to tests/error/parsing/test_comparison.py index a9340f9..a577a8c 100644 --- a/tests/error/parsing/type/test_numeric.py +++ b/tests/error/parsing/test_comparison.py @@ -1,4 +1,4 @@ -from valtypes.error.parsing.type.numeric import ExclusiveMaximum, ExclusiveMinimum, Maximum, Minimum +from valtypes.error.parsing.comparison import ExclusiveMaximum, ExclusiveMinimum, Maximum, Minimum def test_maximum() -> None: diff --git a/tests/error/parsing/test_numeric.py b/tests/error/parsing/test_numeric.py deleted file mode 100644 index f22568c..0000000 --- a/tests/error/parsing/test_numeric.py +++ /dev/null @@ -1,5 +0,0 @@ -from valtypes.error.parsing.numeric import FractionalNumber - - -def test_fractional_number() -> None: - assert str(FractionalNumber(1.5)) == "got fractional number: 1.5" diff --git a/tests/error/parsing/type/test_sized.py b/tests/error/parsing/test_sized.py similarity index 77% rename from tests/error/parsing/type/test_sized.py rename to tests/error/parsing/test_sized.py index afa9d8a..8b613a4 100644 --- a/tests/error/parsing/type/test_sized.py +++ b/tests/error/parsing/test_sized.py @@ -1,4 +1,4 @@ -from valtypes.error.parsing.type.sized import MaximumLength, MinimumLength +from valtypes.error.parsing.sized import MaximumLength, MinimumLength def test_maximum_length() -> None: diff --git a/tests/error/parsing/test_str.py b/tests/error/parsing/test_str.py new file mode 100644 index 0000000..8f9acd0 --- /dev/null +++ b/tests/error/parsing/test_str.py @@ -0,0 +1,21 @@ +import re + +import regex + +from valtypes.error.parsing.str import RegexPatternCompilation, RegexPatternNoMatch, RePatternCompilation, RePatternNoMatch + + +def test_re_pattern_no_match() -> None: + assert str(RePatternNoMatch(re.compile(r"^[a-z]+$"), "123")) == "the value doesn't match the pattern '^[a-z]+$', got: '123'" + + +def test_regex_pattern_no_match() -> None: + assert str(RegexPatternNoMatch(regex.compile(r"^[a-z]+$"), "123")) == "the value doesn't match the pattern '^[a-z]+$', got: '123'" + + +def test_re_pattern_compilation() -> None: + assert str(RePatternCompilation("pattern")) == "can't compile pattern 'pattern'" + + +def test_regex_pattern_compilation() -> None: + assert str(RegexPatternCompilation("pattern")) == "can't compile pattern 'pattern'" diff --git a/tests/error/parsing/type/test_str.py b/tests/error/parsing/type/test_str.py deleted file mode 100644 index c46d688..0000000 --- a/tests/error/parsing/type/test_str.py +++ /dev/null @@ -1,7 +0,0 @@ -import re - -from valtypes.error.parsing.type.str import Pattern - - -def test_pattern() -> None: - assert str(Pattern(re.compile(r"^[a-z]+$"), "123")) == "the value doesn't match the pattern '^[a-z]+$', got: '123'" diff --git a/tests/parsing/parse_json/test_dict_to_dataclass.py b/tests/parsing/parse_json/test_dict_to_dataclass.py index 6486752..bd81376 100644 --- a/tests/parsing/parse_json/test_dict_to_dataclass.py +++ b/tests/parsing/parse_json/test_dict_to_dataclass.py @@ -17,7 +17,7 @@ class Foo: assert parse_json(Foo, {"bar": 1, "baz": "2"}) == Foo(1, "2") -def test_supports_optional_fields() -> None: +def test_does_not_require_optional_fields() -> None: @dataclass class Foo: a: int @@ -30,6 +30,16 @@ class Foo: assert parse_json(Foo, {"a": 1, "c": "c"}) == Foo(1, c="c") +def test_uses_provided_value_rather_than_default() -> None: + @dataclass + class Foo: + a: int = 1 + b: int = field(default=2) + c: int = field(default_factory=lambda: 3) + + assert parse_json(Foo, {"a": 4, "b": 5, "c": 6}) == Foo(4, 5, 6) + + def test_does_not_require_no_init_fields() -> None: @dataclass class Foo: diff --git a/tests/parsing/parse_json/test_float_to_subclass_of_float.py b/tests/parsing/parse_json/test_float_to_subclass_of_float.py index 222af38..75a8658 100644 --- a/tests/parsing/parse_json/test_float_to_subclass_of_float.py +++ b/tests/parsing/parse_json/test_float_to_subclass_of_float.py @@ -1,12 +1,12 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.comparison as error from valtypes import parse_json from valtypes.type import float def test_uses_constructor_to_parse_float_to_subclass_of_float() -> None: - assert parse_json(float.Positive, 2.0) == 2 + assert parse_json(float.Positive, 2.0) == 2.0 with pytest.raises(error.ExclusiveMinimum): parse_json(float.Positive, 0.0) diff --git a/tests/parsing/parse_json/test_int_to_subclass_of_int.py b/tests/parsing/parse_json/test_int_to_subclass_of_int.py index 7d6175d..6745fc6 100644 --- a/tests/parsing/parse_json/test_int_to_subclass_of_int.py +++ b/tests/parsing/parse_json/test_int_to_subclass_of_int.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.comparison as error from valtypes import parse_json from valtypes.type import int diff --git a/tests/parsing/parse_json/test_list_to_subclass_of_list.py b/tests/parsing/parse_json/test_list_to_subclass_of_list.py index 97894d5..cf2d526 100644 --- a/tests/parsing/parse_json/test_list_to_subclass_of_list.py +++ b/tests/parsing/parse_json/test_list_to_subclass_of_list.py @@ -1,7 +1,7 @@ import pytest import valtypes.error.parsing as error -import valtypes.error.parsing.type.sized as sized_error +import valtypes.error.parsing.sized as sized_error from valtypes import parse_json from valtypes.type import list diff --git a/tests/parsing/parse_json/test_str_to_re_pattern.py b/tests/parsing/parse_json/test_str_to_re_pattern.py new file mode 100644 index 0000000..7524484 --- /dev/null +++ b/tests/parsing/parse_json/test_str_to_re_pattern.py @@ -0,0 +1,17 @@ +import re + +import pytest + +import valtypes.error.parsing.str as error +from valtypes import parse_json + + +def test_compiles_str_to_pattern() -> None: + assert parse_json(re.Pattern[str], "abc") == re.compile("abc") + + +def test_raises_error_if_cant_compile_pattern() -> None: + with pytest.raises(error.RePatternCompilation) as info: + parse_json(re.Pattern[str], "(") + + assert info.value == error.RePatternCompilation("(") diff --git a/tests/parsing/parse_json/test_str_to_regex_pattern.py b/tests/parsing/parse_json/test_str_to_regex_pattern.py new file mode 100644 index 0000000..be79dc2 --- /dev/null +++ b/tests/parsing/parse_json/test_str_to_regex_pattern.py @@ -0,0 +1,16 @@ +import pytest +import regex + +import valtypes.error.parsing.str as error +from valtypes import parse_json + + +def test_compiles_str_to_pattern() -> None: + assert parse_json(regex.Pattern[str], "abc") == regex.compile("abc") + + +def test_raises_error_if_cant_compile_pattern() -> None: + with pytest.raises(error.RegexPatternCompilation) as info: + parse_json(regex.Pattern[str], "(") + + assert info.value == error.RegexPatternCompilation("(") diff --git a/tests/parsing/parse_json/test_str_to_subclass_of_str.py b/tests/parsing/parse_json/test_str_to_subclass_of_str.py index eed653d..2d8919d 100644 --- a/tests/parsing/parse_json/test_str_to_subclass_of_str.py +++ b/tests/parsing/parse_json/test_str_to_subclass_of_str.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error from valtypes import parse_json from valtypes.type import str diff --git a/tests/type/float/test_exclusive_maximum.py b/tests/type/float/test_exclusive_maximum.py index efa1c24..39a7ac2 100644 --- a/tests/type/float/test_exclusive_maximum.py +++ b/tests/type/float/test_exclusive_maximum.py @@ -1,26 +1,26 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type class Float(type.ExclusiveMaximum): - __exclusive_maximum__ = 10 + __exclusive_maximum__ = 10.0 def test_raises_error_if_value_is_greater_than_exclusive_maximum() -> None: with pytest.raises(error.ExclusiveMaximum) as info: - Float(11) + Float(11.0) - assert info.value == error.ExclusiveMaximum(10, 11) + assert info.value == error.ExclusiveMaximum(10.0, 11.0) def test_raises_error_if_value_equals_to_exclusive_maximum() -> None: with pytest.raises(error.ExclusiveMaximum) as info: - Float(10) + Float(10.0) - assert info.value == error.ExclusiveMaximum(10, 10) + assert info.value == error.ExclusiveMaximum(10.0, 10.0) def test_succeeds_if_value_is_less_than_exclusive_maximum() -> None: - Float(9) + Float(9.0) diff --git a/tests/type/float/test_exclusive_minimum.py b/tests/type/float/test_exclusive_minimum.py index 9ded6ee..beaed6c 100644 --- a/tests/type/float/test_exclusive_minimum.py +++ b/tests/type/float/test_exclusive_minimum.py @@ -1,26 +1,26 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type class Float(type.ExclusiveMinimum): - __exclusive_minimum__ = 10 + __exclusive_minimum__ = 10.0 def test_raises_error_if_value_is_less_than_exclusive_minimum() -> None: with pytest.raises(error.ExclusiveMinimum) as info: - Float(9) + Float(9.0) - assert info.value == error.ExclusiveMinimum(10, 9) + assert info.value == error.ExclusiveMinimum(10.0, 9.0) def test_raises_error_if_value_equals_to_exclusive_minimum() -> None: with pytest.raises(error.ExclusiveMinimum) as info: - Float(10) + Float(10.0) - assert info.value == error.ExclusiveMinimum(10, 10) + assert info.value == error.ExclusiveMinimum(10.0, 10.0) def test_succeeds_if_value_is_greater_than_exclusive_minimum() -> None: - Float(11) + Float(11.0) diff --git a/tests/type/float/test_maximum.py b/tests/type/float/test_maximum.py index 79cbe33..b7e5f5e 100644 --- a/tests/type/float/test_maximum.py +++ b/tests/type/float/test_maximum.py @@ -1,27 +1,23 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type class Float(type.Maximum): - __maximum__ = 10 + __maximum__ = 10.0 def test_raises_error_if_value_is_greater_than_maximum() -> None: with pytest.raises(error.Maximum) as info: - Float(11) + Float(11.0) - assert info.value == error.Maximum(10, 11) + assert info.value == error.Maximum(10.0, 11.0) def test_raises_error_if_value_equals_to_maximum() -> None: - Float(10) + Float(10.0) def test_succeeds_if_value_is_less_than_maximum() -> None: - """ - It succeeds if the value is less than the maximum - """ - - Float(9) + Float(9.0) diff --git a/tests/type/float/test_minimum.py b/tests/type/float/test_minimum.py index 2a63734..fa7fc3b 100644 --- a/tests/type/float/test_minimum.py +++ b/tests/type/float/test_minimum.py @@ -1,23 +1,23 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type class Float(type.Minimum): - __minimum__ = 10 + __minimum__ = 10.0 def test_raises_error_if_value_is_less_than_minimum() -> None: with pytest.raises(error.Minimum) as info: - Float(9) + Float(9.0) - assert info.value == error.Minimum(10, 9) + assert info.value == error.Minimum(10.0, 9.0) def test_succeeds_if_value_equals_to_minimum() -> None: - Float(10) + Float(10.0) def test_succeeds_if_value_is_greater_than_minimum() -> None: - Float(11) + Float(11.0) diff --git a/tests/type/float/test_negative.py b/tests/type/float/test_negative.py index 102b8d7..177f2ef 100644 --- a/tests/type/float/test_negative.py +++ b/tests/type/float/test_negative.py @@ -1,22 +1,22 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type def test_raises_error_if_value_is_positive() -> None: with pytest.raises(error.ExclusiveMaximum) as info: - type.Negative(1) + type.Negative(1.0) - assert info.value == error.ExclusiveMaximum(0, 1) + assert info.value == error.ExclusiveMaximum(0.0, 1.0) def test_raises_error_if_value_equals_to_0() -> None: with pytest.raises(error.ExclusiveMaximum) as info: - type.Negative(0) + type.Negative(0.0) - assert info.value == error.ExclusiveMaximum(0, 0) + assert info.value == error.ExclusiveMaximum(0.0, 0.0) def test_succeeds_if_value_is_negative() -> None: - type.Negative(-1) + type.Negative(-1.0) diff --git a/tests/type/float/test_non_negative.py b/tests/type/float/test_non_negative.py index 9ae9407..dec4683 100644 --- a/tests/type/float/test_non_negative.py +++ b/tests/type/float/test_non_negative.py @@ -1,19 +1,19 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type def test_raises_error_if_value_is_negative() -> None: with pytest.raises(error.Minimum) as info: - type.NonNegative(-1) + type.NonNegative(-1.0) - assert info.value == error.Minimum(0, -1) + assert info.value == error.Minimum(0.0, -1.0) def test_succeeds_if_value_equals_to_0() -> None: - type.NonNegative(0) + type.NonNegative(0.0) def test_succeeds_if_value_is_positive() -> None: - type.NonNegative(1) + type.NonNegative(1.0) diff --git a/tests/type/float/test_non_positive.py b/tests/type/float/test_non_positive.py index 83338e2..3ec583d 100644 --- a/tests/type/float/test_non_positive.py +++ b/tests/type/float/test_non_positive.py @@ -1,19 +1,19 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type def test_raises_error_if_value_is_positive() -> None: with pytest.raises(error.Maximum) as info: - type.NonPositive(1) + type.NonPositive(1.0) - assert info.value == error.Maximum(0, 1) + assert info.value == error.Maximum(0.0, 1.0) def test_succeeds_if_value_equals_to_0() -> None: - type.NonPositive(0) + type.NonPositive(0.0) def test_succeeds_if_value_is_negative() -> None: - type.NonPositive(-1) + type.NonPositive(-1.0) diff --git a/tests/type/float/test_portion.py b/tests/type/float/test_portion.py index 78d2860..a374da0 100644 --- a/tests/type/float/test_portion.py +++ b/tests/type/float/test_portion.py @@ -1,29 +1,29 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type def test_raises_error_if_value_is_greater_than_1() -> None: with pytest.raises(error.Maximum) as info: - type.Portion(2) + type.Portion(2.0) - assert info.value == error.Maximum(1, 2) + assert info.value == error.Maximum(1.0, 2.0) def test_raises_error_if_value_is_less_than_0() -> None: with pytest.raises(error.Minimum) as info: - type.Portion(-1) + type.Portion(-1.0) - assert info.value == error.Minimum(0, -1) + assert info.value == error.Minimum(0.0, -1.0) def test_succeeds_if_value_equals_to_1() -> None: - type.Portion(1) + type.Portion(1.0) def test_succeeds_if_value_equals_to_0() -> None: - type.Portion(0) + type.Portion(0.0) def test_succeeds_if_value_is_between_0_and_1() -> None: diff --git a/tests/type/float/test_positive.py b/tests/type/float/test_positive.py index caa6166..c78ae0e 100644 --- a/tests/type/float/test_positive.py +++ b/tests/type/float/test_positive.py @@ -1,22 +1,22 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error import valtypes.type.float as type def test_raises_error_if_value_is_negative() -> None: with pytest.raises(error.ExclusiveMinimum) as info: - type.Positive(-1) + type.Positive(-1.0) - assert info.value == error.ExclusiveMinimum(0, -1) + assert info.value == error.ExclusiveMinimum(0.0, -1.0) def test_raises_error_if_value_equals_to_0() -> None: with pytest.raises(error.ExclusiveMinimum) as info: - type.Positive(0) + type.Positive(0.0) - assert info.value == error.ExclusiveMinimum(0, 0) + assert info.value == error.ExclusiveMinimum(0.0, 0.0) def test_succeeds_if_value_is_positive() -> None: - type.Positive(1) + type.Positive(1.0) diff --git a/tests/type/frozenset/test_maximum_length.py b/tests/type/frozenset/test_maximum_length.py index 91f1ceb..773bbf6 100644 --- a/tests/type/frozenset/test_maximum_length.py +++ b/tests/type/frozenset/test_maximum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.frozenset as type diff --git a/tests/type/frozenset/test_minimum_length.py b/tests/type/frozenset/test_minimum_length.py index a9b31e5..274f98d 100644 --- a/tests/type/frozenset/test_minimum_length.py +++ b/tests/type/frozenset/test_minimum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.frozenset as type diff --git a/tests/type/frozenset/test_non_empty.py b/tests/type/frozenset/test_non_empty.py index 384273c..f508d6e 100644 --- a/tests/type/frozenset/test_non_empty.py +++ b/tests/type/frozenset/test_non_empty.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.frozenset as type diff --git a/tests/type/int/test_maximum.py b/tests/type/int/test_maximum.py index 95e6b57..6981c32 100644 --- a/tests/type/int/test_maximum.py +++ b/tests/type/int/test_maximum.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.int as error import valtypes.type.int as type diff --git a/tests/type/int/test_minimum.py b/tests/type/int/test_minimum.py index bde1e69..d96d310 100644 --- a/tests/type/int/test_minimum.py +++ b/tests/type/int/test_minimum.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.int as error import valtypes.type.int as type diff --git a/tests/type/int/test_negative.py b/tests/type/int/test_negative.py index 3ec1415..146f124 100644 --- a/tests/type/int/test_negative.py +++ b/tests/type/int/test_negative.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.int as error import valtypes.type.int as type diff --git a/tests/type/int/test_non_negative.py b/tests/type/int/test_non_negative.py index 184815e..668e0a4 100644 --- a/tests/type/int/test_non_negative.py +++ b/tests/type/int/test_non_negative.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.int as error import valtypes.type.int as type diff --git a/tests/type/int/test_non_positive.py b/tests/type/int/test_non_positive.py index 6bb7157..83935c0 100644 --- a/tests/type/int/test_non_positive.py +++ b/tests/type/int/test_non_positive.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.int as error import valtypes.type.int as type diff --git a/tests/type/int/test_positive.py b/tests/type/int/test_positive.py index ab878bc..221fd14 100644 --- a/tests/type/int/test_positive.py +++ b/tests/type/int/test_positive.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.int as error import valtypes.type.int as type diff --git a/tests/type/list/test_maximum_length.py b/tests/type/list/test_maximum_length.py index f0c23a9..f8acb97 100644 --- a/tests/type/list/test_maximum_length.py +++ b/tests/type/list/test_maximum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.list as type diff --git a/tests/type/list/test_minimum_length.py b/tests/type/list/test_minimum_length.py index 04798be..b79d921 100644 --- a/tests/type/list/test_minimum_length.py +++ b/tests/type/list/test_minimum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.list as type diff --git a/tests/type/list/test_non_empty.py b/tests/type/list/test_non_empty.py index 34ce7c8..1e3b123 100644 --- a/tests/type/list/test_non_empty.py +++ b/tests/type/list/test_non_empty.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.list as type diff --git a/tests/type/set/test_maximum_length.py b/tests/type/set/test_maximum_length.py index d8a95c2..3a376e8 100644 --- a/tests/type/set/test_maximum_length.py +++ b/tests/type/set/test_maximum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.set as type diff --git a/tests/type/set/test_minimum_length.py b/tests/type/set/test_minimum_length.py index eedf818..0b9cc5d 100644 --- a/tests/type/set/test_minimum_length.py +++ b/tests/type/set/test_minimum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.set as type diff --git a/tests/type/set/test_non_empty.py b/tests/type/set/test_non_empty.py index e346726..4ed4ad0 100644 --- a/tests/type/set/test_non_empty.py +++ b/tests/type/set/test_non_empty.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.set as type diff --git a/tests/type/sized/test_maximum_length.py b/tests/type/sized/test_maximum_length.py index 23f0466..59adcaa 100644 --- a/tests/type/sized/test_maximum_length.py +++ b/tests/type/sized/test_maximum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.sized as type diff --git a/tests/type/sized/test_minimum_length.py b/tests/type/sized/test_minimum_length.py index d9de803..e2b464b 100644 --- a/tests/type/sized/test_minimum_length.py +++ b/tests/type/sized/test_minimum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.sized as type diff --git a/tests/type/sized/test_non_empty.py b/tests/type/sized/test_non_empty.py index 4c33990..a41b9b4 100644 --- a/tests/type/sized/test_non_empty.py +++ b/tests/type/sized/test_non_empty.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.sized as type diff --git a/tests/type/str/test_maximum_length.py b/tests/type/str/test_maximum_length.py index 7934eea..6351884 100644 --- a/tests/type/str/test_maximum_length.py +++ b/tests/type/str/test_maximum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.str as type diff --git a/tests/type/str/test_minimum_length.py b/tests/type/str/test_minimum_length.py index cf45c19..6b0f18a 100644 --- a/tests/type/str/test_minimum_length.py +++ b/tests/type/str/test_minimum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.str as type diff --git a/tests/type/str/test_pattern.py b/tests/type/str/test_re_pattern.py similarity index 56% rename from tests/type/str/test_pattern.py rename to tests/type/str/test_re_pattern.py index 49beffc..7267f2b 100644 --- a/tests/type/str/test_pattern.py +++ b/tests/type/str/test_re_pattern.py @@ -2,22 +2,22 @@ import pytest -import valtypes.error.parsing.type.str as error +import valtypes.error.parsing.str as error import valtypes.type.str as type def test_raises_error_if_value_doesnt_match_pattern() -> None: - class Str(type.Pattern): + class Str(type.RePattern): __pattern__ = re.compile("^[a-z]+$") - with pytest.raises(error.Pattern) as info: + with pytest.raises(error.RePatternNoMatch) as info: Str("123") - assert info.value == error.Pattern(re.compile("^[a-z]+$"), "123") + assert info.value == error.RePatternNoMatch(re.compile("^[a-z]+$"), "123") def test_succeeds_if_value_matches_pattern() -> None: - class Str(type.Pattern): + class Str(type.RePattern): __pattern__ = re.compile("^[a-z]+$") Str("abc") diff --git a/tests/type/str/test_regex_pattern.py b/tests/type/str/test_regex_pattern.py new file mode 100644 index 0000000..1f6db51 --- /dev/null +++ b/tests/type/str/test_regex_pattern.py @@ -0,0 +1,22 @@ +import pytest +import regex + +import valtypes.error.parsing.str as error +import valtypes.type.str as type + + +def test_raises_error_if_value_doesnt_match_pattern() -> None: + class Str(type.RegexPattern): + __pattern__ = regex.compile("^[a-z]+$") + + with pytest.raises(error.RegexPatternNoMatch) as info: + Str("123") + + assert info.value == error.RegexPatternNoMatch(regex.compile("^[a-z]+$"), "123") + + +def test_succeeds_if_value_matches_pattern() -> None: + class Str(type.RegexPattern): + __pattern__ = regex.compile("^[a-z]+$") + + Str("abc") diff --git a/tests/type/tuple/test_maximum_length.py b/tests/type/tuple/test_maximum_length.py index aff2dbe..b03fec6 100644 --- a/tests/type/tuple/test_maximum_length.py +++ b/tests/type/tuple/test_maximum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.tuple as type diff --git a/tests/type/tuple/test_minimum_length.py b/tests/type/tuple/test_minimum_length.py index 2aba19f..c060942 100644 --- a/tests/type/tuple/test_minimum_length.py +++ b/tests/type/tuple/test_minimum_length.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.tuple as type diff --git a/tests/type/tuple/test_non_empty.py b/tests/type/tuple/test_non_empty.py index a32b532..9dc44d0 100644 --- a/tests/type/tuple/test_non_empty.py +++ b/tests/type/tuple/test_non_empty.py @@ -1,6 +1,6 @@ import pytest -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error import valtypes.type.tuple as type diff --git a/valtypes/collection.py b/valtypes/collection.py index 069e207..9538bfa 100644 --- a/valtypes/collection.py +++ b/valtypes/collection.py @@ -1,20 +1,18 @@ from collections.abc import Iterator -from typing import Any, Generic, TypeVar +from typing import Generic, Self, TypeVar __all__ = ["Collection"] T = TypeVar("T") -T_Collection = TypeVar("T_Collection", bound="Collection[Any]") - class Collection(Generic[T]): def __init__(self, items: list[T]): self._items = items @classmethod - def empty(cls: type[T_Collection]) -> T_Collection: + def empty(cls) -> Self: return cls([]) def add_to_top(self, *items: T) -> None: diff --git a/valtypes/condition.py b/valtypes/condition.py index 23a7409..b13a7ba 100644 --- a/valtypes/condition.py +++ b/valtypes/condition.py @@ -1,11 +1,15 @@ from __future__ import annotations import abc +import re from collections.abc import Callable from dataclasses import InitVar, dataclass, is_dataclass from types import UnionType from typing import Any, Generic, TypeVar +import regex + +from valtypes.type.network import IPv4, IPv6 from valtypes.typing import Dataclass, GenericAlias, LiteralAlias, UnionAlias from valtypes.util import resolve_type_arguments @@ -16,6 +20,7 @@ "AliasOf", "And", "Decorated", + "Equals", "FromCallable", "InstanceOf", "Is", @@ -34,6 +39,7 @@ "fixed_length_tuple_alias", "generic_alias", "init_var", + "ipv4", "object_is_alias_of_list", "object_is_fixed_length_tuple_alias", "object_is_strict_alias_of_list", @@ -44,6 +50,8 @@ "object_is_strict_subclass_of_str", "object_is_tuple_alias", "object_is_variable_length_tuple_alias", + "re_pattern_alias", + "regex_pattern_alias", "union_alias", "variable_length_tuple_alias", ] @@ -171,6 +179,17 @@ def check(self, value: object, /) -> bool: return value is self._object +@dataclass(init=False, repr=False) +class Equals(ABC[object]): + _object: object + + def __init__(self, object: object): + self._object = object + + def check(self, value: object, /) -> bool: + return value == self._object + + @dataclass(init=False, repr=False) class SubclassOf(ABC[type]): _type: type | UnionType @@ -256,3 +275,10 @@ def dataclass_with_init(value: Dataclass, /) -> bool: object_is_alias_of_list: ObjectIsAliasOf = ObjectIsAliasOf(list) object_is_strict_alias_of_list: ObjectIsStrictAliasOf = ObjectIsStrictAliasOf(list) + +re_pattern_alias: Equals = Equals(re.Pattern[str]) +regex_pattern_alias: Equals = Equals(regex.Pattern[str]) + +ipv4: Is = Is(IPv4) + +ipv6: Is = Is(IPv6) diff --git a/valtypes/error/parsing/type/numeric.py b/valtypes/error/parsing/comparison.py similarity index 54% rename from valtypes/error/parsing/type/numeric.py rename to valtypes/error/parsing/comparison.py index d449c49..b879d85 100644 --- a/valtypes/error/parsing/type/numeric.py +++ b/valtypes/error/parsing/comparison.py @@ -1,5 +1,7 @@ from dataclasses import dataclass -from typing import TypeVar +from typing import Generic, TypeVar + +from valtypes.typing import SupportsReachComparison from . import generic @@ -7,6 +9,7 @@ T = TypeVar("T") +T_contra = TypeVar("T_contra", contravariant=True) class Base(generic.Base): @@ -14,36 +17,36 @@ class Base(generic.Base): @dataclass(repr=False, frozen=True) -class Maximum(Base): - maximum: float - got: float +class Maximum(Base, Generic[T_contra]): + maximum: SupportsReachComparison[T_contra] + got: SupportsReachComparison[T_contra] def __str__(self) -> str: return f"the value must be less than or equal to {self.maximum}, got: {self.got}" @dataclass(repr=False, frozen=True) -class Minimum(Base): - minimum: float - got: float +class Minimum(Base, Generic[T_contra]): + minimum: SupportsReachComparison[T_contra] + got: SupportsReachComparison[T_contra] def __str__(self) -> str: return f"the value must be greater than or equal to {self.minimum}, got: {self.got}" @dataclass(repr=False, frozen=True) -class ExclusiveMaximum(Base): - exclusive_maximum: float - got: float +class ExclusiveMaximum(Base, Generic[T_contra]): + exclusive_maximum: SupportsReachComparison[T_contra] + got: SupportsReachComparison[T_contra] def __str__(self) -> str: return f"the value must be less than {self.exclusive_maximum}, got: {self.got}" @dataclass(repr=False, frozen=True) -class ExclusiveMinimum(Base): - exclusive_minimum: float - got: float +class ExclusiveMinimum(Base, Generic[T_contra]): + exclusive_minimum: SupportsReachComparison[T_contra] + got: SupportsReachComparison[T_contra] def __str__(self) -> str: return f"the value must be greater than {self.exclusive_minimum}, got: {self.got}" diff --git a/valtypes/error/parsing/dataclass.py b/valtypes/error/parsing/dataclass.py index c8bf066..e0132c0 100644 --- a/valtypes/error/parsing/dataclass.py +++ b/valtypes/error/parsing/dataclass.py @@ -12,7 +12,7 @@ class Base(generic.Base): @dataclass(repr=False, frozen=True) -class Composite(ExceptionGroup[Base], generic.Base): +class Composite(ExceptionGroup[Base], Base): errors: Sequence[Base] got: Mapping[str, object] @@ -30,7 +30,7 @@ class WrongFieldValue(ExceptionGroup[generic.Base], Base): got: object def __new__(cls, field: str, cause: generic.Base, got: object) -> Self: - return super().__new__(cls, f"can't parse field {field!r}", [cause]) + return super().__new__(cls, f"can't parse field '{field}'", [cause]) def derive(self, errors: Sequence[generic.Base]) -> Self: return self.__class__(self.field, errors[0], self.got) @@ -41,4 +41,4 @@ class MissingField(Base): field: str def __str__(self) -> str: - return f"required field {self.field!r} is missing" + return f"required field '{self.field}' is missing" diff --git a/valtypes/error/parsing/float.py b/valtypes/error/parsing/float.py new file mode 100644 index 0000000..2754300 --- /dev/null +++ b/valtypes/error/parsing/float.py @@ -0,0 +1,23 @@ +from . import comparison + +__all__ = ["Base", "ExclusiveMaximum", "ExclusiveMinimum", "Maximum", "Minimum"] + + +class Base(comparison.Base): + pass + + +class Maximum(comparison.Maximum[float], Base): + pass + + +class Minimum(comparison.Minimum[float], Base): + pass + + +class ExclusiveMaximum(comparison.ExclusiveMaximum[float], Base): + pass + + +class ExclusiveMinimum(comparison.ExclusiveMinimum[float], Base): + pass diff --git a/valtypes/error/parsing/int.py b/valtypes/error/parsing/int.py new file mode 100644 index 0000000..c986b70 --- /dev/null +++ b/valtypes/error/parsing/int.py @@ -0,0 +1,23 @@ +from . import comparison + +__all__ = ["Base", "ExclusiveMaximum", "ExclusiveMinimum", "Maximum", "Minimum"] + + +class Base(comparison.Base): + pass + + +class Maximum(comparison.Maximum[int], Base): + pass + + +class Minimum(comparison.Minimum[int], Base): + pass + + +class ExclusiveMaximum(comparison.ExclusiveMaximum[int], Base): + pass + + +class ExclusiveMinimum(comparison.ExclusiveMinimum[int], Base): + pass diff --git a/valtypes/error/parsing/literal.py b/valtypes/error/parsing/literal.py index f968eaa..f842454 100644 --- a/valtypes/error/parsing/literal.py +++ b/valtypes/error/parsing/literal.py @@ -12,7 +12,7 @@ class Base(generic.Base): @dataclass(repr=False, frozen=True) -class Composite(ExceptionGroup[Base], generic.Base): +class Composite(ExceptionGroup[Base], Base): errors: Sequence[Base] got: object diff --git a/valtypes/error/parsing/numeric.py b/valtypes/error/parsing/numeric.py deleted file mode 100644 index 9a8edd3..0000000 --- a/valtypes/error/parsing/numeric.py +++ /dev/null @@ -1,17 +0,0 @@ -from dataclasses import dataclass - -from . import generic - -__all__ = ["Base", "FractionalNumber"] - - -class Base(generic.Base): - pass - - -@dataclass(repr=False, frozen=True) -class FractionalNumber(Base): - number: float - - def __str__(self) -> str: - return f"got fractional number: {self.number}" diff --git a/valtypes/error/parsing/sequence.py b/valtypes/error/parsing/sequence.py index 6ab15c8..07d90ab 100644 --- a/valtypes/error/parsing/sequence.py +++ b/valtypes/error/parsing/sequence.py @@ -12,7 +12,7 @@ class Base(generic.Base): @dataclass(repr=False, frozen=True) -class Composite(ExceptionGroup[Base], generic.Base): +class Composite(ExceptionGroup[Base], Base): errors: Sequence[Base] got: object diff --git a/valtypes/error/parsing/type/sized.py b/valtypes/error/parsing/sized.py similarity index 100% rename from valtypes/error/parsing/type/sized.py rename to valtypes/error/parsing/sized.py diff --git a/valtypes/error/parsing/str.py b/valtypes/error/parsing/str.py new file mode 100644 index 0000000..91ae97c --- /dev/null +++ b/valtypes/error/parsing/str.py @@ -0,0 +1,46 @@ +import re +from dataclasses import dataclass + +import regex + +from . import generic + +__all__ = ["Base", "RePatternNoMatch", "RegexPatternNoMatch"] + + +class Base(generic.Base): + pass + + +@dataclass(repr=False, frozen=True) +class RePatternNoMatch(Base): + pattern: re.Pattern[str] + got: str + + def __str__(self) -> str: + return f"the value doesn't match the pattern '{self.pattern.pattern}', got: '{self.got}'" + + +@dataclass(repr=False, frozen=True) +class RegexPatternNoMatch(Base): + pattern: regex.Pattern[str] + got: str + + def __str__(self) -> str: + return f"the value doesn't match the pattern '{self.pattern.pattern}', got: '{self.got}'" + + +@dataclass(repr=False, frozen=True) +class RePatternCompilation(Base): + got: str + + def __str__(self) -> str: + return f"can't compile pattern '{self.got}'" + + +@dataclass(repr=False, frozen=True) +class RegexPatternCompilation(Base): + got: str + + def __str__(self) -> str: + return f"can't compile pattern '{self.got}'" diff --git a/valtypes/error/parsing/type/__init__.py b/valtypes/error/parsing/type/__init__.py deleted file mode 100644 index 9ea0f64..0000000 --- a/valtypes/error/parsing/type/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .generic import Base - -__all__ = ["Base"] diff --git a/valtypes/error/parsing/type/generic.py b/valtypes/error/parsing/type/generic.py deleted file mode 100644 index a71ab7e..0000000 --- a/valtypes/error/parsing/type/generic.py +++ /dev/null @@ -1,7 +0,0 @@ -from valtypes.error.parsing import generic - -__all__ = ["Base"] - - -class Base(generic.Base): - pass diff --git a/valtypes/error/parsing/type/str.py b/valtypes/error/parsing/type/str.py deleted file mode 100644 index 8b99f44..0000000 --- a/valtypes/error/parsing/type/str.py +++ /dev/null @@ -1,19 +0,0 @@ -import re -from dataclasses import dataclass - -from . import generic - -__all__ = ["Base", "Pattern"] - - -class Base(generic.Base): - pass - - -@dataclass(repr=False, frozen=True) -class Pattern(Base): - pattern: re.Pattern[str] - got: str - - def __str__(self) -> str: - return f"the value doesn't match the pattern '{self.pattern.pattern}', got: '{self.got}'" diff --git a/valtypes/forward_ref.py b/valtypes/forward_ref.py index 6bab898..19a6961 100644 --- a/valtypes/forward_ref.py +++ b/valtypes/forward_ref.py @@ -51,4 +51,4 @@ def _frame(self) -> FrameType: return self.__args__[1] def __repr__(self) -> str: - return f"{self._code!r}" + return f"'{self._code}'" diff --git a/valtypes/parsing/__init__.py b/valtypes/parsing/__init__.py index 5908c37..e4b1528 100644 --- a/valtypes/parsing/__init__.py +++ b/valtypes/parsing/__init__.py @@ -1,3 +1,4 @@ from .parse_json import parse_json +from .rule import Rule -__all__ = ["parse_json"] +__all__ = ["Rule", "parse_json"] diff --git a/valtypes/parsing/factory/__init__.py b/valtypes/parsing/factory/__init__.py index eef67cd..8d06d5f 100644 --- a/valtypes/parsing/factory/__init__.py +++ b/valtypes/parsing/factory/__init__.py @@ -1,5 +1,6 @@ -from .abc import ABC, Preparse +from .base import ABC, Preparse from .composite import Composite +from .const import Const from .dict_to_dataclass import DictToDataclass from .from_callable import FromCallable from .from_json import from_json @@ -16,6 +17,7 @@ __all__ = [ "ABC", "Composite", + "Const", "DictToDataclass", "FromCallable", "IterableToList", diff --git a/valtypes/parsing/factory/abc.py b/valtypes/parsing/factory/base.py similarity index 100% rename from valtypes/parsing/factory/abc.py rename to valtypes/parsing/factory/base.py diff --git a/valtypes/parsing/factory/composite.py b/valtypes/parsing/factory/composite.py index b4e8ea8..c9a5312 100644 --- a/valtypes/parsing/factory/composite.py +++ b/valtypes/parsing/factory/composite.py @@ -1,16 +1,14 @@ from __future__ import annotations from functools import cached_property -from typing import TYPE_CHECKING, TypeVar +from typing import TypeVar from valtypes import error from valtypes.collection import Collection from valtypes.parsing import parser +from valtypes.parsing.rule import Rule -if TYPE_CHECKING: - from valtypes.parsing import rule - -from .abc import ABC +from .base import ABC __all__ = ["Composite"] @@ -20,7 +18,7 @@ F = TypeVar("F") -class Composite(ABC[object, T, F], Collection["rule.ABC[T, F]"]): +class Composite(ABC[object, T, F], Collection[Rule[T, F]]): def get_parser_for(self, type: object, /) -> parser.ABC[T, F]: if type not in self._cache: self._cache[type] = self._find_parser_for(type) @@ -32,7 +30,7 @@ def _find_parser_for(self, type: object, /) -> parser.ABC[T, F]: return rule.get_parser_for(type) raise error.NoParser(type) - def add_to_top(self, *rules: rule.ABC[T, F]) -> None: + def add_to_top(self, *rules: Rule[T, F]) -> None: self._cache.clear() super().add_to_top(*rules) diff --git a/valtypes/parsing/factory/const.py b/valtypes/parsing/factory/const.py new file mode 100644 index 0000000..6c2faab --- /dev/null +++ b/valtypes/parsing/factory/const.py @@ -0,0 +1,19 @@ +from typing import TypeVar + +from valtypes.parsing import parser + +from .base import ABC + +__all__ = ["Const"] + + +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class Const(ABC[object, T_contra, T_co]): + def __init__(self, parser: parser.ABC[T_contra, T_co]): + self._parser = parser + + def get_parser_for(self, type: object, /) -> parser.ABC[T_contra, T_co]: + return self._parser diff --git a/valtypes/parsing/factory/dict_to_dataclass.py b/valtypes/parsing/factory/dict_to_dataclass.py index 90403b9..9c616b2 100644 --- a/valtypes/parsing/factory/dict_to_dataclass.py +++ b/valtypes/parsing/factory/dict_to_dataclass.py @@ -7,7 +7,7 @@ from valtypes.parsing import parser from valtypes.typing import Dataclass -from .abc import ABC +from .base import ABC __all__ = ["DictToDataclass", "Factory"] diff --git a/valtypes/parsing/factory/from_callable.py b/valtypes/parsing/factory/from_callable.py index 16732d8..137b49d 100644 --- a/valtypes/parsing/factory/from_callable.py +++ b/valtypes/parsing/factory/from_callable.py @@ -3,7 +3,7 @@ from valtypes.parsing import parser -from .abc import ABC +from .base import ABC __all__ = ["FromCallable"] diff --git a/valtypes/parsing/factory/from_json.py b/valtypes/parsing/factory/from_json.py index 4cf449f..2ed79d0 100644 --- a/valtypes/parsing/factory/from_json.py +++ b/valtypes/parsing/factory/from_json.py @@ -1,9 +1,15 @@ +import re from typing import Any +import regex + from valtypes import condition -from valtypes.parsing import parser, rule +from valtypes.parsing import parser +from valtypes.parsing.rule import Rule +from valtypes.type.network import IPv4, IPv6 from .composite import Composite +from .const import Const from .from_callable import FromCallable from .object_to_dataclass import ObjectToDataclass from .object_to_list import ObjectToList @@ -30,18 +36,26 @@ object_to_union_alias: ToUnion[object, Any] = ToUnion(from_json) object_to_literal: ToLiteral[object, Any] = ToLiteral(from_json) object_to_type: FromCallable[type, object, Any] = FromCallable(parser.ObjectToType) +object_to_re_pattern: Const[object, re.Pattern[str]] = Const(parser.object_to_re_pattern) +object_to_regex_pattern: Const[object, regex.Pattern[str]] = Const(parser.object_to_regex_pattern) +object_to_ipv4: Const[object, IPv4] = Const(parser.object_to_ipv4) +object_to_ipv6: Const[object, IPv6] = Const(parser.object_to_ipv6) from_json.add_to_top( - rule.Base(object_to_init_var, condition.init_var), - rule.Base(object_to_dataclass, condition.dataclass_with_init), - rule.Base(object_to_subclass_of_int, condition.object_is_strict_subclass_of_int), - rule.Base(object_to_subclass_of_float, condition.object_is_strict_subclass_of_float), - rule.Base(object_to_subclass_of_str, condition.object_is_strict_subclass_of_str), - rule.Base(object_to_subclass_of_bytes, condition.object_is_strict_subclass_of_bytes), - rule.Base(object_to_subclass_of_bytearray, condition.object_is_strict_subclass_of_bytearray), - rule.Base(object_to_list_alias, condition.object_is_strict_alias_of_list), - rule.Base(object_to_subclass_of_list_alias, condition.object_is_alias_of_list), - rule.Base(object_to_union_alias, condition.union_alias), - rule.Base(object_to_literal, condition.literal_alias), - rule.Base(object_to_type, condition.builtin_type), + Rule(object_to_init_var, condition.init_var), + Rule(object_to_dataclass, condition.dataclass_with_init), + Rule(object_to_subclass_of_int, condition.object_is_strict_subclass_of_int), + Rule(object_to_subclass_of_float, condition.object_is_strict_subclass_of_float), + Rule(object_to_subclass_of_str, condition.object_is_strict_subclass_of_str), + Rule(object_to_subclass_of_bytes, condition.object_is_strict_subclass_of_bytes), + Rule(object_to_subclass_of_bytearray, condition.object_is_strict_subclass_of_bytearray), + Rule(object_to_list_alias, condition.object_is_strict_alias_of_list), + Rule(object_to_subclass_of_list_alias, condition.object_is_alias_of_list), + Rule(object_to_union_alias, condition.union_alias), + Rule(object_to_literal, condition.literal_alias), + Rule(object_to_type, condition.builtin_type), + Rule(object_to_re_pattern, condition.re_pattern_alias), + Rule(object_to_regex_pattern, condition.regex_pattern_alias), + Rule(object_to_ipv4, condition.ipv4), + Rule(object_to_ipv6, condition.ipv6), ) diff --git a/valtypes/parsing/factory/iterable_to_list.py b/valtypes/parsing/factory/iterable_to_list.py index c617b87..0ad6f3c 100644 --- a/valtypes/parsing/factory/iterable_to_list.py +++ b/valtypes/parsing/factory/iterable_to_list.py @@ -4,7 +4,7 @@ from valtypes.parsing import parser from valtypes.util import resolve_type_arguments -from .abc import ABC +from .base import ABC __all__ = ["IterableToList"] diff --git a/valtypes/parsing/factory/mapping_to_dict.py b/valtypes/parsing/factory/mapping_to_dict.py index 0a4469c..9d53fbe 100644 --- a/valtypes/parsing/factory/mapping_to_dict.py +++ b/valtypes/parsing/factory/mapping_to_dict.py @@ -4,7 +4,7 @@ from valtypes.parsing import parser from valtypes.util import resolve_type_arguments -from .abc import ABC +from .base import ABC __all__ = ["MappingToDict"] diff --git a/valtypes/parsing/factory/object_to_dataclass.py b/valtypes/parsing/factory/object_to_dataclass.py index d07d5dc..86536bc 100644 --- a/valtypes/parsing/factory/object_to_dataclass.py +++ b/valtypes/parsing/factory/object_to_dataclass.py @@ -2,7 +2,7 @@ from valtypes.parsing import parser -from .abc import ABC +from .base import ABC from .dict_to_dataclass import DictToDataclass from .shortcut import Shortcut diff --git a/valtypes/parsing/factory/object_to_list.py b/valtypes/parsing/factory/object_to_list.py index e00c4a3..e3bbe7d 100644 --- a/valtypes/parsing/factory/object_to_list.py +++ b/valtypes/parsing/factory/object_to_list.py @@ -2,7 +2,7 @@ from valtypes.parsing import parser -from .abc import ABC +from .base import ABC from .iterable_to_list import IterableToList from .shortcut import Shortcut diff --git a/valtypes/parsing/factory/shortcut.py b/valtypes/parsing/factory/shortcut.py index a710e38..08c27d3 100644 --- a/valtypes/parsing/factory/shortcut.py +++ b/valtypes/parsing/factory/shortcut.py @@ -2,7 +2,7 @@ from valtypes.parsing import parser -from .abc import ABC +from .base import ABC __all__ = ["Shortcut"] diff --git a/valtypes/parsing/factory/to_init_var.py b/valtypes/parsing/factory/to_init_var.py index b7250b2..647cfbe 100644 --- a/valtypes/parsing/factory/to_init_var.py +++ b/valtypes/parsing/factory/to_init_var.py @@ -2,7 +2,7 @@ from valtypes.parsing import parser -from .abc import ABC +from .base import ABC __all__ = ["ToInitVar"] diff --git a/valtypes/parsing/factory/to_literal.py b/valtypes/parsing/factory/to_literal.py index 96041f6..c97254a 100644 --- a/valtypes/parsing/factory/to_literal.py +++ b/valtypes/parsing/factory/to_literal.py @@ -3,7 +3,7 @@ from valtypes.parsing import parser from valtypes.typing import LiteralAlias -from .abc import ABC +from .base import ABC __all__ = ["ToLiteral"] diff --git a/valtypes/parsing/factory/to_subclass_of.py b/valtypes/parsing/factory/to_subclass_of.py index 5a05a00..2a1678e 100644 --- a/valtypes/parsing/factory/to_subclass_of.py +++ b/valtypes/parsing/factory/to_subclass_of.py @@ -2,7 +2,7 @@ from valtypes.parsing import parser -from .abc import ABC +from .base import ABC __all__ = ["ToSubclassOf"] diff --git a/valtypes/parsing/factory/to_subclass_of_generic.py b/valtypes/parsing/factory/to_subclass_of_generic.py index 38a3f36..1539316 100644 --- a/valtypes/parsing/factory/to_subclass_of_generic.py +++ b/valtypes/parsing/factory/to_subclass_of_generic.py @@ -3,7 +3,7 @@ from valtypes.parsing import parser from valtypes.util import resolve_type_arguments -from .abc import ABC +from .base import ABC __all__ = ["ToSubclassOfGeneric"] diff --git a/valtypes/parsing/factory/to_union.py b/valtypes/parsing/factory/to_union.py index d7fd4a5..138fd2b 100644 --- a/valtypes/parsing/factory/to_union.py +++ b/valtypes/parsing/factory/to_union.py @@ -3,7 +3,7 @@ from valtypes.parsing import parser -from .abc import ABC +from .base import ABC __all__ = ["ToUnion"] diff --git a/valtypes/parsing/parser/__init__.py b/valtypes/parsing/parser/__init__.py index 1f55509..86134c0 100644 --- a/valtypes/parsing/parser/__init__.py +++ b/valtypes/parsing/parser/__init__.py @@ -1,9 +1,17 @@ -from .abc import ABC, Chain +from .base import ABC, Chain from .dict_to_dataclass import DictToDataclass from .from_callable import FromCallable from .iterable_to_list import IterableToList from .mapping_to_dict import MappingToDict +from .match_to_ipv4 import match_to_ipv4 +from .match_to_ipv6 import match_to_ipv6 +from .misc import object_to_dataclass_fields_dict, object_to_ipv4, object_to_ipv6, object_to_re_pattern, object_to_regex_pattern from .object_to_type import ObjectToType +from .str_to_ipv4 import str_to_ipv4 +from .str_to_re_match import StrToReMatch +from .str_to_re_pattern import str_to_re_pattern +from .str_to_regex_match import StrToRegexMatch +from .str_to_regex_pattern import str_to_regex_pattern from .to_literal import ToLiteral from .to_literal_choice import ToLiteralChoice from .to_literal_choice_preparse import ToLiteralChoicePreparse @@ -17,12 +25,20 @@ "IterableToList", "MappingToDict", "ObjectToType", + "StrToReMatch", + "StrToRegexMatch", "ToLiteral", "ToLiteralChoice", "ToLiteralChoicePreparse", "ToUnion", + "match_to_ipv4", + "match_to_ipv6", "object_to_dataclass_fields_dict", + "object_to_ipv4", + "object_to_ipv6", + "object_to_re_pattern", + "object_to_regex_pattern", + "str_to_ipv4", + "str_to_re_pattern", + "str_to_regex_pattern", ] - - -object_to_dataclass_fields_dict: Chain[object, dict[str, object]] = ObjectToType(dict) >> MappingToDict(ObjectToType(str), ObjectToType(object)) diff --git a/valtypes/parsing/parser/abc.py b/valtypes/parsing/parser/base.py similarity index 100% rename from valtypes/parsing/parser/abc.py rename to valtypes/parsing/parser/base.py diff --git a/valtypes/parsing/parser/dict_to_dataclass.py b/valtypes/parsing/parser/dict_to_dataclass.py index 18da469..62d2aba 100644 --- a/valtypes/parsing/parser/dict_to_dataclass.py +++ b/valtypes/parsing/parser/dict_to_dataclass.py @@ -4,7 +4,7 @@ import valtypes.error.parsing as error import valtypes.error.parsing.dataclass as dataclass_error -from .abc import ABC +from .base import ABC __all__ = ["DictToDataclass", "Parser"] @@ -56,7 +56,7 @@ def _field_required(self, field_name: str) -> bool: def _parse_field(self, field_name: str) -> None: try: - self._fields[field_name] = self._required_fields_parsers[field_name].parse(self._source[field_name]) + self._fields[field_name] = self._fields_parsers[field_name].parse(self._source[field_name]) except error.Base as e: self._errors.append(dataclass_error.WrongFieldValue(field_name, e, self._source[field_name])) diff --git a/valtypes/parsing/parser/from_callable.py b/valtypes/parsing/parser/from_callable.py index bad8f70..e453dd5 100644 --- a/valtypes/parsing/parser/from_callable.py +++ b/valtypes/parsing/parser/from_callable.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from typing import TypeVar -from .abc import ABC +from .base import ABC __all__ = ["FromCallable"] diff --git a/valtypes/parsing/parser/iterable_to_list.py b/valtypes/parsing/parser/iterable_to_list.py index 8a0629d..e1a88df 100644 --- a/valtypes/parsing/parser/iterable_to_list.py +++ b/valtypes/parsing/parser/iterable_to_list.py @@ -5,7 +5,7 @@ import valtypes.error.parsing as error import valtypes.error.parsing.sequence as sequence_error -from .abc import ABC +from .base import ABC __all__ = ["IterableToList", "Parser"] diff --git a/valtypes/parsing/parser/mapping_to_dict.py b/valtypes/parsing/parser/mapping_to_dict.py index e54fdf2..42c2b55 100644 --- a/valtypes/parsing/parser/mapping_to_dict.py +++ b/valtypes/parsing/parser/mapping_to_dict.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from typing import TypeVar -from .abc import ABC +from .base import ABC __all__ = ["MappingToDict"] diff --git a/valtypes/parsing/parser/match_to_ipv4.py b/valtypes/parsing/parser/match_to_ipv4.py new file mode 100644 index 0000000..b5b48c7 --- /dev/null +++ b/valtypes/parsing/parser/match_to_ipv4.py @@ -0,0 +1,14 @@ +from regex import Match + +from valtypes.type.network import IPv4, Octet + +from .from_callable import FromCallable + +__all__ = ["match_to_ipv4"] + + +@FromCallable +def match_to_ipv4(match: Match[str]) -> IPv4: + raw_octets = match.captures("octet") + octets = [Octet(octet) for octet in raw_octets] + return IPv4(*octets) diff --git a/valtypes/parsing/parser/match_to_ipv6.py b/valtypes/parsing/parser/match_to_ipv6.py new file mode 100644 index 0000000..752f5a6 --- /dev/null +++ b/valtypes/parsing/parser/match_to_ipv6.py @@ -0,0 +1,86 @@ +from collections.abc import Iterable + +from regex import Match + +from valtypes.type.network import Hexet, IPv4, IPv6 + +from .from_callable import FromCallable +from .str_to_ipv4 import str_to_ipv4 + +__all__ = ["Parser", "match_to_ipv6"] + + +@FromCallable +def match_to_ipv6(match: Match[str]) -> IPv6: + return Parser(match).parse() + + +class Parser: + def __init__(self, match: Match[str]): + self._match = match + + def parse(self) -> IPv6: + return IPv6(*self._hexets_before_elision, *self._elided_zero_valued_hexets, *self._hexets_after_elision, *self._ipv4_hexets) + + @property + def _hexets_before_elision(self) -> list[Hexet]: + return [Hexet(raw_hexet) for raw_hexet in self._raw_hexets_before_elision] + + @property + def _raw_hexets_before_elision(self) -> list[str]: + return self._raw_hexets[: self._elision_hexet_index] + + @property + def _elided_zero_valued_hexets(self) -> list[Hexet]: + return [Hexet(0)] * self._elided_hexets_count + + @property + def _hexets_after_elision(self) -> list[Hexet]: + return [Hexet(raw_hexet) for raw_hexet in self._raw_hexets_after_elision] + + @property + def _raw_hexets_after_elision(self) -> list[str]: + return self._raw_hexets[self._elision_hexet_index :] + + @property + def _elision_hexet_index(self) -> int: + for hexet_index, hexet_end_position in enumerate(self._hexets_end_positions): + if hexet_end_position == self._elision_start_position: + return hexet_index + 1 + return 0 + + @property + def _hexets_end_positions(self) -> list[int]: + return [span[1] for span in self._match.spans("hexet")] + + @property + def _elision_start_position(self) -> int: + return self._match.span("elision")[0] + + @property + def _elided_hexets_count(self) -> int: + return self._target_hexets_count - len(self._raw_hexets) + + @property + def _target_hexets_count(self) -> int: + return 6 if self._has_ipv4 else 8 + + @property + def _raw_hexets(self) -> list[str]: + return self._match.captures("hexet") + + @property + def _ipv4_hexets(self) -> Iterable[Hexet]: + return self._ipv4.hexets if self._has_ipv4 else () + + @property + def _has_ipv4(self) -> bool: + return bool(self._match["ipv4"]) + + @property + def _ipv4(self) -> IPv4: + return str_to_ipv4.parse(self._raw_ipv4) + + @property + def _raw_ipv4(self) -> str: + return self._match["ipv4"] diff --git a/valtypes/parsing/parser/misc.py b/valtypes/parsing/parser/misc.py new file mode 100644 index 0000000..e5c7ddb --- /dev/null +++ b/valtypes/parsing/parser/misc.py @@ -0,0 +1,28 @@ +import re + +import regex + +from valtypes.pattern import network as pattern +from valtypes.type.network import IPv4, IPv6 + +from .base import Chain +from .mapping_to_dict import MappingToDict +from .match_to_ipv6 import match_to_ipv6 +from .object_to_type import ObjectToType +from .str_to_ipv4 import str_to_ipv4 +from .str_to_re_pattern import str_to_re_pattern +from .str_to_regex_match import StrToRegexMatch +from .str_to_regex_pattern import str_to_regex_pattern + +__all__ = ["object_to_dataclass_fields_dict", "object_to_ipv4", "object_to_ipv6", "object_to_re_pattern", "object_to_regex_pattern"] + + +object_to_dataclass_fields_dict: Chain[object, dict[str, object]] = ObjectToType(dict) >> MappingToDict(ObjectToType(str), ObjectToType(object)) + +object_to_re_pattern: Chain[object, re.Pattern[str]] = ObjectToType(str) >> str_to_re_pattern + +object_to_regex_pattern: Chain[object, regex.Pattern[str]] = ObjectToType(str) >> str_to_regex_pattern + +object_to_ipv4: Chain[object, IPv4] = ObjectToType(str) >> str_to_ipv4 + +object_to_ipv6: Chain[object, IPv6] = ObjectToType(str) >> StrToRegexMatch(pattern.ipv6) >> match_to_ipv6 diff --git a/valtypes/parsing/parser/object_to_type.py b/valtypes/parsing/parser/object_to_type.py index e0a9fbc..52fb9c1 100644 --- a/valtypes/parsing/parser/object_to_type.py +++ b/valtypes/parsing/parser/object_to_type.py @@ -3,7 +3,7 @@ import valtypes.error.parsing as error -from .abc import ABC +from .base import ABC __all__ = ["ObjectToType"] diff --git a/valtypes/parsing/parser/str_to_ipv4.py b/valtypes/parsing/parser/str_to_ipv4.py new file mode 100644 index 0000000..9f5b08d --- /dev/null +++ b/valtypes/parsing/parser/str_to_ipv4.py @@ -0,0 +1,11 @@ +from valtypes.pattern import network as pattern +from valtypes.type.network import IPv4 + +from .base import Chain +from .match_to_ipv4 import match_to_ipv4 +from .str_to_regex_match import StrToRegexMatch + +__all__ = ["str_to_ipv4"] + + +str_to_ipv4: Chain[str, IPv4] = StrToRegexMatch(pattern.ipv4) >> match_to_ipv4 diff --git a/valtypes/parsing/parser/str_to_re_match.py b/valtypes/parsing/parser/str_to_re_match.py new file mode 100644 index 0000000..624bc0a --- /dev/null +++ b/valtypes/parsing/parser/str_to_re_match.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass +from re import Match, Pattern + +import valtypes.error.parsing.str as error + +from .base import ABC + +__all__ = ["StrToReMatch"] + + +@dataclass(init=False, repr=False) +class StrToReMatch(ABC[str, Match[str]]): + def __init__(self, pattern: Pattern[str]): + self._pattern = pattern + + def parse(self, source: str, /) -> Match[str]: + if match := self._pattern.fullmatch(source): + return match + raise error.RePatternNoMatch(self._pattern, source) diff --git a/valtypes/parsing/parser/str_to_re_pattern.py b/valtypes/parsing/parser/str_to_re_pattern.py new file mode 100644 index 0000000..93f2c88 --- /dev/null +++ b/valtypes/parsing/parser/str_to_re_pattern.py @@ -0,0 +1,16 @@ +import re +from re import Pattern + +import valtypes.error.parsing.str as error + +from .from_callable import FromCallable + +__all__ = ["str_to_re_pattern"] + + +@FromCallable +def str_to_re_pattern(pattern: str) -> Pattern[str]: + try: + return re.compile(pattern) + except re.error: + raise error.RePatternCompilation(pattern) from None diff --git a/valtypes/parsing/parser/str_to_regex_match.py b/valtypes/parsing/parser/str_to_regex_match.py new file mode 100644 index 0000000..19a4ec5 --- /dev/null +++ b/valtypes/parsing/parser/str_to_regex_match.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass + +from regex import Match, Pattern + +import valtypes.error.parsing.str as error + +from .base import ABC + +__all__ = ["StrToRegexMatch"] + + +@dataclass(init=False, repr=False) +class StrToRegexMatch(ABC[str, Match[str]]): + def __init__(self, pattern: Pattern[str]): + self._pattern = pattern + + def parse(self, source: str, /) -> Match[str]: + if match := self._pattern.fullmatch(source): + return match + raise error.RegexPatternNoMatch(self._pattern, source) diff --git a/valtypes/parsing/parser/str_to_regex_pattern.py b/valtypes/parsing/parser/str_to_regex_pattern.py new file mode 100644 index 0000000..868ed4e --- /dev/null +++ b/valtypes/parsing/parser/str_to_regex_pattern.py @@ -0,0 +1,16 @@ +import regex +from regex import Pattern + +import valtypes.error.parsing.str as error + +from .from_callable import FromCallable + +__all__ = ["str_to_regex_pattern"] + + +@FromCallable +def str_to_regex_pattern(pattern: str) -> Pattern[str]: + try: + return regex.compile(pattern) + except regex.error: + raise error.RegexPatternCompilation(pattern) from None diff --git a/valtypes/parsing/parser/to_literal.py b/valtypes/parsing/parser/to_literal.py index 787579f..18aab97 100644 --- a/valtypes/parsing/parser/to_literal.py +++ b/valtypes/parsing/parser/to_literal.py @@ -4,7 +4,7 @@ import valtypes.error.parsing.literal as literal_error -from .abc import ABC +from .base import ABC from .to_literal_choice_preparse import ToLiteralChoicePreparse __all__ = ["ToLiteral"] diff --git a/valtypes/parsing/parser/to_literal_choice.py b/valtypes/parsing/parser/to_literal_choice.py index 4291049..cb5003c 100644 --- a/valtypes/parsing/parser/to_literal_choice.py +++ b/valtypes/parsing/parser/to_literal_choice.py @@ -2,7 +2,7 @@ import valtypes.error.parsing.literal as error -from .abc import ABC +from .base import ABC __all__ = ["ToLiteralChoice"] diff --git a/valtypes/parsing/parser/to_literal_choice_preparse.py b/valtypes/parsing/parser/to_literal_choice_preparse.py index f6ce320..4a560b5 100644 --- a/valtypes/parsing/parser/to_literal_choice_preparse.py +++ b/valtypes/parsing/parser/to_literal_choice_preparse.py @@ -3,7 +3,7 @@ import valtypes.error.parsing as error import valtypes.error.parsing.literal as literal_error -from .abc import ABC +from .base import ABC from .to_literal_choice import ToLiteralChoice __all__ = ["ToLiteralChoicePreparse"] diff --git a/valtypes/parsing/parser/to_union.py b/valtypes/parsing/parser/to_union.py index 48ac1e0..70bf51c 100644 --- a/valtypes/parsing/parser/to_union.py +++ b/valtypes/parsing/parser/to_union.py @@ -5,7 +5,7 @@ import valtypes.error.parsing as error -from .abc import ABC +from .base import ABC __all__ = ["Parser", "ToUnion"] diff --git a/valtypes/parsing/rule/base.py b/valtypes/parsing/rule.py similarity index 59% rename from valtypes/parsing/rule/base.py rename to valtypes/parsing/rule.py index 9f242f7..36da87d 100644 --- a/valtypes/parsing/rule/base.py +++ b/valtypes/parsing/rule.py @@ -1,21 +1,18 @@ -from __future__ import annotations - -from typing import Any, TypeVar +from typing import Any, Generic, TypeVar from valtypes import condition -from valtypes.parsing import factory, parser - -from .abc import ABC +from valtypes.parsing import parser +from valtypes.parsing.factory import ABC as ABCFactory -__all__ = ["Base"] +__all__ = ["Rule"] T_co = TypeVar("T_co", covariant=True) T_contra = TypeVar("T_contra", contravariant=True) -class Base(ABC[T_contra, T_co]): - def __init__(self, factory: factory.ABC[Any, T_contra, T_co], type_condition: condition.ABC[object]): +class Rule(Generic[T_contra, T_co]): + def __init__(self, factory: ABCFactory[Any, T_contra, T_co], type_condition: condition.ABC[object]): self._factory = factory self._type_condition = type_condition diff --git a/valtypes/parsing/rule/__init__.py b/valtypes/parsing/rule/__init__.py deleted file mode 100644 index d1c06a1..0000000 --- a/valtypes/parsing/rule/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .abc import ABC -from .base import Base - -__all__ = ["ABC", "Base"] diff --git a/valtypes/parsing/rule/abc.py b/valtypes/parsing/rule/abc.py deleted file mode 100644 index b8499f4..0000000 --- a/valtypes/parsing/rule/abc.py +++ /dev/null @@ -1,20 +0,0 @@ -import abc -from typing import Any, Generic, TypeVar - -from valtypes.parsing import parser - -__all__ = ["ABC"] - - -T_co = TypeVar("T_co", covariant=True) -T_contra = TypeVar("T_contra", contravariant=True) - - -class ABC(abc.ABC, Generic[T_contra, T_co]): - @abc.abstractmethod - def is_suitable_for(self, type: object) -> bool: - pass - - @abc.abstractmethod - def get_parser_for(self, type: Any) -> parser.ABC[T_contra, T_co]: - pass diff --git a/tests/error/parsing/type/__init__.py b/valtypes/pattern/__init__.py similarity index 100% rename from tests/error/parsing/type/__init__.py rename to valtypes/pattern/__init__.py diff --git a/valtypes/pattern/network.py b/valtypes/pattern/network.py new file mode 100644 index 0000000..53806be --- /dev/null +++ b/valtypes/pattern/network.py @@ -0,0 +1,43 @@ +from regex import Pattern, compile + +from valtypes.regex import network as regex + +__all__ = [ + "absolute_or_empty_path", + "absolute_path", + "fragment", + "ipv4", + "ipv6", + "ipvfuture_content", + "no_scheme_path", + "query", + "reg_name", + "rootless_path", + "scheme", + "user_info", +] + + +ipv4: Pattern[str] = compile(regex.ipv4) + +ipv6: Pattern[str] = compile(regex.ipv6) + +ipvfuture_content: Pattern[str] = compile(regex.ipvfuture_content) + +scheme: Pattern[str] = compile(regex.scheme) + +user_info: Pattern[str] = compile(regex.user_info) + +reg_name: Pattern[str] = compile(regex.reg_name) + +absolute_or_empty_path: Pattern[str] = compile(regex.absolute_or_empty_path) + +absolute_path: Pattern[str] = compile(regex.absolute_path) + +no_scheme_path: Pattern[str] = compile(regex.no_scheme_path) + +rootless_path: Pattern[str] = compile(regex.rootless_path) + +query: Pattern[str] = compile(regex.query) + +fragment: Pattern[str] = compile(regex.fragment) diff --git a/valtypes/regex/__init__.py b/valtypes/regex/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/valtypes/regex/network.py b/valtypes/regex/network.py new file mode 100644 index 0000000..b253a99 --- /dev/null +++ b/valtypes/regex/network.py @@ -0,0 +1,99 @@ +__all__ = [ + "absolute_or_empty_path", + "absolute_path", + "fragment", + "ipv4", + "ipv6", + "ipvfuture_content", + "no_scheme_path", + "query", + "reg_name", + "rootless_path", + "scheme", + "user_info", +] + + +octet = r"(?P\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])" + +ipv4 = rf"(?P{octet}\.{octet}\.{octet}\.{octet})" + +hexadecimal_digit = r"[0-9A-Fa-f]" + +hexet = rf"(?P{hexadecimal_digit}{{1,4}})" + +last_32_bits = rf"(?:{hexet}:{hexet}|{ipv4})" + +elision = r"(?P::)" + +ipv6 = ( + rf"(?:" + rf"(?:{hexet}:){{6}}{last_32_bits}" + rf"|(?:{elision}(?:{hexet}:){{5}}{last_32_bits})" + rf"|{hexet}?{elision}(?:{hexet}:){{4}}{last_32_bits}" + rf"|(?:(?:{hexet}:)?{hexet})?{elision}(?:{hexet}:){{3}}{last_32_bits}" + rf"|(?:(?:{hexet}:){{0,2}}{hexet})?{elision}(?:{hexet}:){{2}}{last_32_bits}" + rf"|(?:(?:{hexet}:){{0,3}}{hexet})?{elision}{hexet}:{last_32_bits}" + rf"|(?:(?:{hexet}:){{0,4}}{hexet})?{elision}{last_32_bits}" + rf"|(?:(?:{hexet}:){{0,5}}{hexet})?{elision}{hexet}" + rf"|(?:(?:{hexet}:){{0,6}}{hexet})?{elision}" + rf")" +) + +unreserved = r"(?:[0-9A-Fa-z-._~])" + +pct_encoded = rf"(?:%{hexadecimal_digit}{hexadecimal_digit})" + +sub_delims = r"(?:[!$&'()*+,;=])" + +pchar = rf"(?:{unreserved}|{pct_encoded}|{sub_delims}|:|@)" + +ipvfuture_content = rf"(?:(?:{unreserved}|{sub_delims}|:)+)" + +ipvfuture = rf"(?:[Vv]{hexadecimal_digit}+\.{ipvfuture_content})" + +scheme = r"(?:[A-Za-z](?:[A-Za-z]|\d|\+|-|\.)*)" + +user_info = rf"(?:(?:{unreserved}|{pct_encoded}|{sub_delims}|:)*)" + +ip_literal = rf"(?:\[(?:{ipv6}|{ipvfuture})\])" + +reg_name = rf"(?:(?:{unreserved}|{pct_encoded}|{sub_delims})*)" + +host = rf"(?:{ip_literal}|{ipv4}|{reg_name})" + +port = r"(?:\d*)" + +authority = rf"(?:(?:{user_info}@)?{host}(?::{port})?)" + +segment = rf"(?:{pchar}*)" + +non_empty_segment = rf"(?:{pchar}+)" + +non_empty_no_colon_segment = rf"(?:(?:{unreserved}|{pct_encoded}|{sub_delims}|@)+)" + +absolute_or_empty_path = rf"(?:(?:/{segment})*)" + +absolute_path = rf"(?:/(?:{non_empty_segment}(?:/{segment})*)?)" + +no_scheme_path = rf"(?:{non_empty_no_colon_segment}(?:/{segment})*)" + +rootless_path = rf"(?:{non_empty_segment}(?:/{segment})*)" + +empty_path = r"(?:)" + +query = rf"(?:(?:{pchar}|/|\?)*)" + +fragment = rf"(?:(?:{pchar}|/|\?)*)" + +hier_part = rf"(?://{authority}{absolute_or_empty_path}|{absolute_path}|{rootless_path}|{empty_path})" + +relative_part = rf"(?://{authority}{absolute_or_empty_path}|{absolute_path}|{no_scheme_path}|{empty_path})" + +absolute_uri = rf"(?:{scheme}:{hier_part}(?:\?{query})?)" + +uri = rf"(?:{scheme}:{hier_part}(?:\?{query})?(?:#{fragment})?)" + +relative_reference = rf"(?:{relative_part}(?:\?{query})?(?:#{fragment})?)" + +uri_reference = rf"(?:{uri}|{relative_reference})" diff --git a/valtypes/type/float.py b/valtypes/type/float.py index 1984d32..331ccac 100644 --- a/valtypes/type/float.py +++ b/valtypes/type/float.py @@ -1,11 +1,12 @@ from typing import ClassVar, SupportsFloat, SupportsIndex -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.float as error from valtypes.typing import ReadableBuffer from . import generic __all__ = [ + "Any", "ExclusiveMaximum", "ExclusiveMinimum", "InitHook", @@ -19,8 +20,11 @@ ] +Any = float + + class InitHook(generic.InitHook, float): - def __init__(self, x: SupportsFloat | SupportsIndex | str | ReadableBuffer = 0.0, /): + def __init__(self, _: SupportsFloat | SupportsIndex | str | ReadableBuffer = 0.0, /): super().__init__() diff --git a/valtypes/type/frozenset.py b/valtypes/type/frozenset.py index 9ee7bad..0dede70 100644 --- a/valtypes/type/frozenset.py +++ b/valtypes/type/frozenset.py @@ -3,14 +3,17 @@ from . import generic, sized -__all__ = ["InitHook", "MaximumLength", "MinimumLength", "NonEmpty"] +__all__ = ["Any", "InitHook", "MaximumLength", "MinimumLength", "NonEmpty"] T_co = TypeVar("T_co", covariant=True) +Any = frozenset[T_co] + + class InitHook(generic.InitHook, frozenset[T_co]): - def __init__(self, iterable: Iterable[T_co] = frozenset(), /): + def __init__(self, _: Iterable[T_co] = frozenset(), /): super().__init__() diff --git a/valtypes/type/int.py b/valtypes/type/int.py index 5eb8396..95525eb 100644 --- a/valtypes/type/int.py +++ b/valtypes/type/int.py @@ -1,11 +1,12 @@ from typing import ClassVar, SupportsIndex, SupportsInt, overload -import valtypes.error.parsing.type.numeric as error +import valtypes.error.parsing.int as error from valtypes.typing import ReadableBuffer, SupportsTrunc from . import generic __all__ = [ + "Any", "InitHook", "Maximum", "Minimum", @@ -16,16 +17,19 @@ ] +Any = int + + class InitHook(generic.InitHook, int): @overload - def __init__(self, x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ..., /): + def __init__(self, _: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ..., /): ... @overload - def __init__(self, x: str | bytes | bytearray, /, base: SupportsIndex): + def __init__(self, _: str | bytes | bytearray, /, base: SupportsIndex): ... - def __init__(self, x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = 0, /, base: SupportsIndex = 10): + def __init__(self, _: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = 0, /, base: SupportsIndex = 10): super().__init__() diff --git a/valtypes/type/list.py b/valtypes/type/list.py index 0404b42..ac6293d 100644 --- a/valtypes/type/list.py +++ b/valtypes/type/list.py @@ -1,16 +1,20 @@ +import typing from collections.abc import Iterable -from typing import Any, Self, SupportsIndex, TypeVar, cast, overload +from typing import Self, SupportsIndex, TypeVar, cast, overload from valtypes.util import ensure_sequence, get_slice_length from . import generic, sized -__all__ = ["InitHook", "LengthHook", "MaximumLength", "MinimumLength", "NonEmpty"] +__all__ = ["Any", "InitHook", "LengthHook", "MaximumLength", "MinimumLength", "NonEmpty"] T = TypeVar("T") +Any = list[T] + + class InitHook(generic.InitHook, list[T]): def __init__(self, iterable: Iterable[T] = [], /): super().__init__(iterable) @@ -54,7 +58,7 @@ def __setitem__(self, item: SupportsIndex | slice, object: T | Iterable[T], /) - if isinstance(item, slice): object = ensure_sequence(cast(Iterable[T], object)) self.__notify_length_delta__(len(object) - get_slice_length(item, self)) - super().__setitem__(cast(Any, item), cast(Any, object)) + super().__setitem__(cast(typing.Any, item), cast(typing.Any, object)) def __delitem__(self, item: SupportsIndex | slice, /) -> None: if isinstance(item, slice): diff --git a/valtypes/type/network.py b/valtypes/type/network.py new file mode 100644 index 0000000..49eface --- /dev/null +++ b/valtypes/type/network.py @@ -0,0 +1,251 @@ +from dataclasses import dataclass +from typing import ClassVar, Final, Literal + +from regex import Pattern + +from valtypes.pattern import network as pattern + +from . import int, str + +__all__ = [ + "AbsoluteOrEmptyPath", + "AbsolutePath", + "AbsoluteURI", + "AbsoluteURIWithAuthority", + "AbsoluteURIWithoutAuthority", + "Authority", + "EmptyPath", + "Fragment", + "Hexet", + "Host", + "IPLiteral", + "IPv4", + "IPv6", + "IPvFuture", + "IPvFutureContent", + "IPvFutureVersion", + "NoSchemePath", + "Octet", + "Port", + "Query", + "RegName", + "RelativeReference", + "RelativeReferenceWithAuthority", + "RelativeReferenceWithoutAuthority", + "RootlessPath", + "Scheme", + "URI", + "URIReference", + "URIWithAuthority", + "URIWithoutAuthority", + "UserInfo", +] + + +class Octet(int.Minimum, int.Maximum): + __minimum__: ClassVar[int.Any] = 0 + __maximum__: ClassVar[int.Any] = 255 + + +class Hexet(int.Minimum, int.Maximum): + __minimum__: ClassVar[int.Any] = 0 + __maximum__: ClassVar[int.Any] = 65565 + + def __str__(self) -> str.Any: + return f"{self:x}" + + +@dataclass(init=False, unsafe_hash=True) +class IPv4: + octets: tuple[Octet, Octet, Octet, Octet] + + def __init__(self, *octets: * tuple[Octet, Octet, Octet, Octet]): + self.octets: Final = octets + + @property + def hexets(self) -> tuple[Hexet, Hexet]: + return Hexet(self.octets[0] << 0o10 | self.octets[1]), Hexet(self.octets[2] << 0o10 | self.octets[3]) + + def __str__(self) -> str.Any: + return ".".join(map(str.Any, self.octets)) + + +@dataclass(init=False, unsafe_hash=True) +class IPv6: + hexets: tuple[Hexet, Hexet, Hexet, Hexet, Hexet, Hexet, Hexet, Hexet] + + def __init__(self, *hexets: * tuple[Hexet, Hexet, Hexet, Hexet, Hexet, Hexet, Hexet, Hexet]): + self.hexets: Final = hexets + + @property + def ipv4(self) -> IPv4: + return IPv4(Octet(self.hexets[-2] >> 0o10), Octet(self.hexets[-2] & 0xFF), Octet(self.hexets[-1] >> 0o10), Octet(self.hexets[-1] & 0xFF)) + + def __str__(self) -> str.Any: + return ":".join(map(str.Any, self.hexets)) + + +class IPvFutureVersion(int.NonNegative): + def __str__(self) -> str.Any: + return f"v{self:x}" + + +class IPvFutureContent(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.ipvfuture_content + + +@dataclass(unsafe_hash=True, frozen=True) +class IPvFuture: + version: IPvFutureVersion + content: IPvFutureContent + + def __str__(self) -> str.Any: + return f"v{self.version}.{self.content}" + + +class Scheme(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.scheme + + +class UserInfo(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.user_info + + +class RegName(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.reg_name + + +IPLiteral = IPv6 | IPvFuture + + +Host = IPv4 | IPLiteral | RegName + + +Port = int.NonNegative + + +@dataclass(kw_only=True, unsafe_hash=True, frozen=True) +class Authority: + user_info: UserInfo | None + host: Host + port: Port | None + + def __str__(self) -> str.Any: + user_info_part = "" if self.user_info is None else f"{self.user_info}@" + host_part = f"[{self.host}]" if isinstance(self.host, IPLiteral) else f"{self.host}" + port_part = "" if self.port is None else f":{self.port}" + return f"{user_info_part}{host_part}{port_part}" + + +class AbsoluteOrEmptyPath(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.absolute_or_empty_path + + +class AbsolutePath(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.absolute_path + + +class NoSchemePath(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.no_scheme_path + + +class RootlessPath(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.rootless_path + + +EmptyPath = Literal[""] + + +class Query(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.query + + +class Fragment(str.RegexPattern): + __pattern__: ClassVar[Pattern[str.Any]] = pattern.fragment + + +@dataclass(kw_only=True, unsafe_hash=True, frozen=True) +class AbsoluteURIWithAuthority: + scheme: Scheme + authority: Authority + path: AbsoluteOrEmptyPath + query: Query | None + + def __str__(self) -> str.Any: + query_part = "" if self.query is None else f"?{self.query}" + return f"{self.scheme}://{self.authority}{self.path}{query_part}" + + +@dataclass(kw_only=True, unsafe_hash=True, frozen=True) +class AbsoluteURIWithoutAuthority: + scheme: Scheme + path: AbsolutePath | RootlessPath | EmptyPath + query: Query | None + + def __str__(self) -> str.Any: + query_part = "" if self.query is None else f"?{self.query}" + return f"{self.scheme}:{self.path}{query_part}" + + +AbsoluteURI = AbsoluteURIWithAuthority | AbsoluteURIWithoutAuthority + + +@dataclass(kw_only=True, unsafe_hash=True, frozen=True) +class URIWithAuthority: + scheme: Scheme + authority: Authority + path: AbsoluteOrEmptyPath + query: Query | None + fragment: Fragment | None + + def __str__(self) -> str.Any: + query_part = "" if self.query is None else f"?{self.query}" + fragment_part = "" if self.fragment is None else f"#{self.fragment}" + return f"{self.scheme}://{self.authority}{self.path}{query_part}{fragment_part}" + + +@dataclass(kw_only=True, unsafe_hash=True, frozen=True) +class URIWithoutAuthority: + scheme: Scheme + path: AbsolutePath | RootlessPath | EmptyPath + query: Query | None + fragment: Fragment | None + + def __str__(self) -> str.Any: + query_part = "" if self.query is None else f"?{self.query}" + fragment_part = "" if self.fragment is None else f"#{self.fragment}" + return f"{self.scheme}:{self.path}{query_part}{fragment_part}" + + +URI = URIWithAuthority | URIWithoutAuthority + + +@dataclass(kw_only=True, unsafe_hash=True, frozen=True) +class RelativeReferenceWithAuthority: + authority: Authority + path: AbsoluteOrEmptyPath + query: Query | None + fragment: Fragment | None + + def __str__(self) -> str.Any: + query_part = "" if self.query is None else f"?{self.query}" + fragment_part = "" if self.fragment is None else f"#{self.fragment}" + return f"//{self.authority}{self.path}{query_part}{fragment_part}" + + +@dataclass(kw_only=True, unsafe_hash=True, frozen=True) +class RelativeReferenceWithoutAuthority: + path: AbsolutePath | NoSchemePath | EmptyPath + query: Query | None + fragment: Fragment | None + + def __str__(self) -> str.Any: + query_part = "" if self.query is None else f"?{self.query}" + fragment_part = "" if self.fragment is None else f"#{self.fragment}" + return f"{self.path}{query_part}{fragment_part}" + + +RelativeReference = RelativeReferenceWithAuthority | RelativeReferenceWithoutAuthority + + +URIReference = URI | RelativeReference diff --git a/valtypes/type/set.py b/valtypes/type/set.py index 738fa52..bbcc7bd 100644 --- a/valtypes/type/set.py +++ b/valtypes/type/set.py @@ -1,16 +1,20 @@ +import typing from collections.abc import Iterable, Set -from typing import Any, Self, TypeVar +from typing import Self, TypeVar from valtypes.util import ensure_iterable_not_iterator from . import generic, sized -__all__ = ["InitHook", "LengthHook", "MaximumLength", "MinimumLength", "NonEmpty"] +__all__ = ["Any", "InitHook", "LengthHook", "MaximumLength", "MinimumLength", "NonEmpty"] T = TypeVar("T") +Any = Set[T] + + class InitHook(generic.InitHook, set[T]): def __init__(self, iterable: Iterable[T] = set(), /): super().__init__(iterable) @@ -22,7 +26,7 @@ def add(self, element: T, /) -> None: self.__notify_length_increments__() super().add(element) - def difference_update(self, *iterables: Iterable[Any]) -> None: + def difference_update(self, *iterables: Iterable[typing.Any]) -> None: iterables = tuple(ensure_iterable_not_iterator(iterable) for iterable in iterables) self.__length_hook__(len(self.difference(*iterables))) super().difference_update(*iterables) @@ -32,7 +36,7 @@ def discard(self, element: T, /) -> None: self.__notify_length_decrements__() super().discard(element) - def intersection_update(self, *iterables: Iterable[Any]) -> None: + def intersection_update(self, *iterables: Iterable[typing.Any]) -> None: iterables = tuple(ensure_iterable_not_iterator(iterable) for iterable in iterables) self.__length_hook__(len(self.intersection(*iterables))) super().intersection_update(*iterables) @@ -67,7 +71,7 @@ def __ior__(self, other: Set[T], /) -> Self: self.__length_hook__(len(self | other)) return super().__ior__(other) - def __isub__(self, other: Set[Any], /) -> Self: + def __isub__(self, other: Set[typing.Any], /) -> Self: self.__length_hook__(len(self - other)) return super().__isub__(other) diff --git a/valtypes/type/sized.py b/valtypes/type/sized.py index 7ffd2ba..82a933e 100644 --- a/valtypes/type/sized.py +++ b/valtypes/type/sized.py @@ -2,7 +2,7 @@ from collections.abc import Sized from typing import ClassVar, TypeVar -import valtypes.error.parsing.type.sized as error +import valtypes.error.parsing.sized as error from valtypes.util import super_endpoint from . import generic diff --git a/valtypes/type/str.py b/valtypes/type/str.py index 68a13c9..6ad22b6 100644 --- a/valtypes/type/str.py +++ b/valtypes/type/str.py @@ -1,15 +1,20 @@ import re from typing import ClassVar -import valtypes.error.parsing.type.str as error +import regex + +import valtypes.error.parsing.str as error from . import generic, sized -__all__ = ["InitHook", "MaximumLength", "MinimumLength", "NonEmpty", "Pattern"] +__all__ = ["Any", "InitHook", "MaximumLength", "MinimumLength", "NonEmpty", "RePattern", "RegexPattern"] + + +Any = str class InitHook(generic.InitHook, str): - def __init__(self, object: object = "", /): + def __init__(self, _: object = "", /): super().__init__() @@ -25,9 +30,17 @@ class NonEmpty(MinimumLength, sized.NonEmpty): pass -class Pattern(InitHook): +class RePattern(InitHook): __pattern__: ClassVar[re.Pattern[str]] def __init_hook__(self) -> None: if not self.__pattern__.fullmatch(self): - raise error.Pattern(self.__pattern__, self) + raise error.RePatternNoMatch(self.__pattern__, self) + + +class RegexPattern(InitHook): + __pattern__: ClassVar[regex.Pattern[str]] + + def __init_hook__(self) -> None: + if not self.__pattern__.fullmatch(self): + raise error.RegexPatternNoMatch(self.__pattern__, self) diff --git a/valtypes/type/tuple.py b/valtypes/type/tuple.py index 6d8e932..baa9ffe 100644 --- a/valtypes/type/tuple.py +++ b/valtypes/type/tuple.py @@ -3,14 +3,17 @@ from . import generic, sized -__all__ = ["InitHook", "MaximumLength", "MinimumLength", "NonEmpty"] +__all__ = ["Any", "InitHook", "MaximumLength", "MinimumLength", "NonEmpty"] T_co = TypeVar("T_co", covariant=True) +Any = tuple[T_co, ...] + + class InitHook(generic.InitHook, tuple[T_co, ...]): - def __init__(self, iterable: Iterable[T_co] = (), /): + def __init__(self, _: Iterable[T_co] = (), /): super().__init__() diff --git a/valtypes/typing.py b/valtypes/typing.py index 74d0d2e..80a177c 100644 --- a/valtypes/typing.py +++ b/valtypes/typing.py @@ -21,12 +21,16 @@ "GenericAlias", "LiteralAlias", "ReadableBuffer", + "SupportsGT", + "SupportsLT", + "SupportsReachComparison", "SupportsTrunc", "UnionAlias", ] T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) class SupportsTrunc(Protocol): @@ -34,6 +38,19 @@ def __trunc__(self) -> int: ... +class SupportsLT(Protocol[T_contra]): + def __lt__(self, _: T_contra, /) -> bool: + ... + + +class SupportsGT(Protocol[T_contra]): + def __gt__(self, _: T_contra, /) -> bool: + ... + + +SupportsReachComparison = SupportsGT[T_contra] | SupportsLT[T_contra] + + @runtime_checkable class Descriptor(Protocol[T_co]): def __get__(self, instance: object, owner: type | None = ...) -> T_co: