pax_global_header00006660000000000000000000000064151237436160014522gustar00rootroot0000000000000052 comment=b9f979055f9dd8cf2efdca77ce70eeda1ce0ba79 django-choices-field-4.0.0/000077500000000000000000000000001512374361600154615ustar00rootroot00000000000000django-choices-field-4.0.0/.coveragerc000066400000000000000000000003441512374361600176030ustar00rootroot00000000000000[run] source = graphene_django_plus omit = .venv/** [report] precision = 2 exclude_lines = pragma: nocover pragma:nocover if TYPE_CHECKING: @overload @abstractmethod @abc.abstractmethod assert_never django-choices-field-4.0.0/.flake8000066400000000000000000000002351512374361600166340ustar00rootroot00000000000000[flake8] extend-ignore = E731,SIM106,R504 max-line-length = 100 exclude = .eggs,.git,.hg,.mypy_cache,.tox,.venv,venv,__pycached__,_build,buck-out,build,dist django-choices-field-4.0.0/.github/000077500000000000000000000000001512374361600170215ustar00rootroot00000000000000django-choices-field-4.0.0/.github/workflows/000077500000000000000000000000001512374361600210565ustar00rootroot00000000000000django-choices-field-4.0.0/.github/workflows/cicd.yml000066400000000000000000000047241512374361600225120ustar00rootroot00000000000000--- name: Run Tests # yamllint disable-line rule:truthy on: push: branches: - master pull_request: branches: - master release: types: - released jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 - uses: pre-commit/action@v3.0.1 with: extra_args: -a tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: django-version: - 4.2.* - 5.0.* - 5.1.* - 5.2.* python-version: - '3.10' - '3.11' - '3.12' - '3.13' - '3.14' exclude: # Django 4.2 only supports python 3.8-3.12 - django-version: 4.2.* python-version: '3.13' - django-version: 4.2.* python-version: '3.14' # Django 5.0 only supports python 3.10-3.12 - django-version: 5.0.* python-version: '3.13' - django-version: 5.0.* python-version: '3.14' # Django 5.1 only supports python 3.10-3.13 - django-version: 5.1.* python-version: '3.14' # Django 5.2 only supports python 3.10-3.13 (assuming) - django-version: 5.2.* python-version: '3.14' steps: - name: Checkout uses: actions/checkout@v4 - name: Install Poetry run: pipx install poetry - name: Set up Python ${{ matrix.python-version }} id: setup-python uses: actions/setup-python@v5 with: cache: poetry python-version: ${{ matrix.python-version }} - name: Install Deps run: poetry install - name: Install Django ${{ matrix.django-version }} run: poetry run pip install "django==${{ matrix.django-version }}" - name: Test with pytest run: poetry run pytest --showlocals -vvv --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} publish: runs-on: ubuntu-latest needs: - lint - tests if: > needs.lint.result == 'success' && needs.tests.result == 'success' && github.event.action == 'released' steps: - uses: actions/checkout@v4 - name: Build and publish to pypi uses: JRubics/poetry-publish@v2.0 with: pypi_token: ${{ secrets.PYPI_TOKEN }} django-choices-field-4.0.0/.gitignore000066400000000000000000000023641512374361600174560ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # ide files .idea/* # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 media celerybeat-schedule.db celerybeat.pid # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ django-choices-field-4.0.0/.pre-commit-config.yaml000066400000000000000000000014211512374361600217400ustar00rootroot00000000000000--- repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer exclude: snapshots - id: check-added-large-files args: - --maxkb=1024 - id: check-docstring-first - id: check-merge-conflict - id: check-yaml exclude: mkdocs.yml - id: check-toml - id: check-json - id: check-xml - id: check-symlinks - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.10 hooks: - id: ruff-format - id: ruff args: - --fix - repo: https://github.com/adamchainz/django-upgrade rev: 1.29.1 hooks: - id: django-upgrade args: - --target-version=4.2 django-choices-field-4.0.0/.vim/000077500000000000000000000000001512374361600163325ustar00rootroot00000000000000django-choices-field-4.0.0/.vim/coc-settings.json000066400000000000000000000003661512374361600216340ustar00rootroot00000000000000{ "pyright.organizeimports.provider": "isort", "python.formatting.provider": "black", "python.linting.enabled": true, "python.linting.flake8Enabled": true, "python.linting.pylintEnabled": false, "python.pythonPath": ".venv/python3" } django-choices-field-4.0.0/LICENSE000066400000000000000000000020651512374361600164710ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2018 Tomás Fox Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. django-choices-field-4.0.0/README.md000066400000000000000000000054061512374361600167450ustar00rootroot00000000000000# django-choices-field [![build status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fbellini666%2Fdjango-choices-field%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/bellini666/django-choices-field/goto?ref=master) [![coverage](https://img.shields.io/codecov/c/github/bellini666/django-choices-field.svg)](https://codecov.io/gh/bellini666/django-choices-field) [![PyPI version](https://img.shields.io/pypi/v/django-choices-field.svg)](https://pypi.org/project/django-choices-field/) ![python version](https://img.shields.io/pypi/pyversions/django-choices-field.svg) ![django version](https://img.shields.io/pypi/djversions/django-choices-field.svg) Django field that set/get django's new TextChoices/IntegerChoices enum. ## Install ```bash pip install django-choices-field ``` ## Usage ```python import enum from django.db import models from django_choices_field import TextChoicesField, IntegerChoicesField, IntegerChoicesFlag class MyModel(models.Model): class TextEnum(models.TextChoices): FOO = "foo", "Foo Description" BAR = "bar", "Bar Description" class IntegerEnum(models.IntegerChoices): FIRST = 1, "First Description" SECOND = 2, "Second Description" class IntegerFlagEnum(IntegerChoicesFlag): FIRST = enum.auto(), "First Option" SECOND = enum.auto(), "Second Option" THIRD = enum.auto(), "Third Option" text_field = TextChoicesField( choices_enum=TextEnum, default=TextEnum.FOO, ) integer_field = IntegerChoicesField( choices_enum=IntegerEnum, default=IntegerEnum.FIRST, ) flag_field = IntegerChoicesFlagField( choices_enum=IntegerFlagEnum, default=IntegerFlagEnum.FIRST | IntegerFlagEnum.SECOND, ) obj = MyModel() reveal_type(obj.text_field) # MyModel.TextEnum.FOO assert isinstance(obj.text_field, MyModel.TextEnum) assert obj.text_field == "foo" reveal_type(obj.integer_field) # MyModel.IntegerEnum.FIRST assert isinstance(obj.integer_field, MyModel.IntegerEnum) assert obj.integer_field == 1 reveal_type(obj.flag_field) # MyModel.IntegerFlagEnum.FIRST | MyModel.IntegerFlagEnum.SECOND assert isinstance(obj.integer_field, MyModel.IntegerFlagEnum) assert obj.flag_field == 3 ``` NOTE: The `IntegerChoicesFlag` requires python 3.11+ to work properly. ## License This project is licensed under MIT licence (see `LICENSE` for more info) ## Contributing Make sure to have [poetry](https://python-poetry.org/) installed. Install dependencies with: ```bash poetry install ``` Run the testsuite with: ```bash poetry run pytest ``` Feel free to fork the project and send me pull requests with new features, corrections and translations. I'll gladly merge them and release new versions ASAP. django-choices-field-4.0.0/django_choices_field/000077500000000000000000000000001512374361600215635ustar00rootroot00000000000000django-choices-field-4.0.0/django_choices_field/__init__.py000066400000000000000000000002741512374361600236770ustar00rootroot00000000000000from .fields import IntegerChoicesField, TextChoicesField from .types import IntegerChoicesFlag __all__ = [ "IntegerChoicesField", "IntegerChoicesFlag", "TextChoicesField", ] django-choices-field-4.0.0/django_choices_field/fields.py000066400000000000000000000222771512374361600234150ustar00rootroot00000000000000import functools import itertools from collections.abc import Callable, Sequence from typing import ( ClassVar, cast, ) from django.core.exceptions import ImproperlyConfigured, ValidationError from django.db import models from .types import IntegerChoicesFlag def _get_flag_description(descs: Sequence[str]) -> str: return "|".join(str(desc) for desc in descs) def _get_integer_enum_members(choices: list[tuple[int | None, str]]) -> dict[str, int]: # choices can contain the `None` key which can't be mapped to an enum. See # Django Model Field docs about Enumeration Types for more info about # labelling empty states with `__empty__`. filtered_choices = [(k, v) for (k, v) in choices if k is not None] return {desc.replace(" ", "_").upper(): value for value, desc in filtered_choices} try: from django.utils.functional import Promise, lazy except ImportError: # pragma: nocover Promise = None _get_flag_description_lazy = None else: _get_flag_description_lazy = cast( "Callable[[Sequence[str]], str]", lazy(_get_flag_description, str), ) class TextChoicesField(models.CharField): """A CharField that validates and stores values from a TextChoices enum. This field ensures that only valid values from the specified TextChoices enum are accepted, providing type safety and validation at the database level. """ description: ClassVar[str] = "TextChoices" default_error_messages: ClassVar[dict[str, str]] = { "invalid": "“%(value)s” must be a subclass of %(enum)s.", } def __init__( self, choices_enum: type[models.TextChoices] | None = None, verbose_name: str | None = None, name: str | None = None, **kwargs, ): if choices_enum is not None: self.choices_enum = choices_enum if getattr(self, "null", False) or kwargs.get("null"): kwargs["choices"] = choices_enum.choices else: kwargs["choices"] = [ (k, v) for (k, v) in choices_enum.choices if cast("object", k) is not None ] elif "choices" in kwargs: self.choices_enum = models.TextChoices( "ChoicesEnum", [(k, (k, v)) for k, v in kwargs["choices"] if k is not None], ) else: raise TypeError("either of choices_enum or choices must be provided") kwargs.setdefault( "max_length", max(len(c[0]) for c in kwargs["choices"] if c[0] is not None), ) super().__init__(verbose_name=verbose_name, name=name, **kwargs) if self.blank and not self.null: raise ImproperlyConfigured( f"{self.__class__.__name__} with blank=True must also have null=True.", ) def to_python(self, value): if value in self.empty_values: # type: ignore[attr-defined] return None try: return self.choices_enum(value) # type: ignore[operator] except ValueError as e: raise ValidationError( self.error_messages["invalid"], code="invalid", params={"value": value, "enum": self.choices_enum}, ) from e def from_db_value(self, value, expression, connection): return self.to_python(value) def get_prep_value(self, value): value = super().get_prep_value(value) return self.to_python(value) class IntegerChoicesField(models.IntegerField): """An IntegerField that validates and stores values from an IntegerChoices enum. This field ensures that only valid integer values from the specified IntegerChoices enum are accepted, providing type safety and validation at the database level. """ description: ClassVar[str] = "IntegerChoices" default_error_messages: ClassVar[dict[str, str]] = { "invalid": "“%(value)s” must be a subclass of %(enum)s.", } def __init__( self, choices_enum: type[models.IntegerChoices] | None = None, verbose_name: str | None = None, name: str | None = None, **kwargs, ): if choices_enum is not None: self.choices_enum = choices_enum if getattr(self, "null", False) or kwargs.get("null"): kwargs["choices"] = choices_enum.choices else: kwargs["choices"] = [ (k, v) for (k, v) in choices_enum.choices if cast("object", k) is not None ] elif "choices" in kwargs: enum_members = _get_integer_enum_members(kwargs["choices"]) self.choices_enum = models.IntegerChoices("ChoicesEnum", enum_members) else: raise TypeError("either of choices_enum or choices must be provided") super().__init__(verbose_name=verbose_name, name=name, **kwargs) if self.blank and not self.null: raise ImproperlyConfigured( f"{self.__class__.__name__} with blank=True must also have null=True.", ) def to_python(self, value): if value is None: return None try: return self.choices_enum(int(value) if isinstance(value, str) else value) except ValueError as e: raise ValidationError( self.error_messages["invalid"], code="invalid", params={"value": value, "enum": self.choices_enum}, ) from e def from_db_value(self, value, expression, connection): return self.to_python(value) def get_prep_value(self, value): value = super().get_prep_value(value) return self.to_python(value) def formfield(self, **kwargs): # pragma:nocover return super().formfield( **{ "coerce": self.to_python, **kwargs, }, ) class IntegerChoicesFlagField(models.IntegerField): """An IntegerField that validates and stores bitwise flag values from an IntegerChoicesFlag enum. This field supports storing combinations of flags from the specified IntegerChoicesFlag enum, allowing multiple enum values to be combined using bitwise operations. """ description: ClassVar[str] = "IntegerChoicesFlag" default_error_messages: ClassVar[dict[str, str]] = { "invalid": "“%(value)s” must be a subclass of %(enum)s.", } def __init__( self, choices_enum: type[IntegerChoicesFlag] | None = None, verbose_name: str | None = None, name: str | None = None, **kwargs, ): if choices_enum is not None: self.choices_enum = choices_enum choices: list[tuple[int | None, str]] if getattr(self, "null", False) or kwargs.get("null"): choices = list(choices_enum.choices) else: choices = [ (k, v) for (k, v) in choices_enum.choices if cast("object", k) is not None ] default_choices = [(x.value, x.label) for x in choices_enum] for i in range(1, len(default_choices)): for combination in itertools.combinations(default_choices, i + 1): value = functools.reduce(lambda a, b: a | b[0], combination, 0) descs = [c[1] for c in combination] if Promise is not None and any(isinstance(desc, Promise) for desc in descs): assert _get_flag_description_lazy is not None desc = _get_flag_description_lazy(descs) else: desc = _get_flag_description(descs) choices.append((value, desc)) kwargs["choices"] = choices elif "choices" in kwargs: default_choices_length = len(kwargs["choices"]).bit_length() default_choices = [kwargs["choices"][i] for i in range(default_choices_length)] enum_members = _get_integer_enum_members(default_choices) self.choices_enum = models.IntegerChoices("ChoicesEnum", enum_members) else: raise TypeError("either of choices_enum or choices must be provided") super().__init__(verbose_name=verbose_name, name=name, **kwargs) if self.blank and not self.null: raise ImproperlyConfigured( f"{self.__class__.__name__} with blank=True must also have null=True.", ) def to_python(self, value): if value is None: return None try: return self.choices_enum(int(value) if isinstance(value, str) else value) except ValueError as e: raise ValidationError( self.error_messages["invalid"], code="invalid", params={"value": value, "enum": self.choices_enum}, ) from e def from_db_value(self, value, expression, connection): return self.to_python(value) def get_prep_value(self, value): value = super().get_prep_value(value) return self.to_python(value) def formfield(self, **kwargs): # pragma:nocover return super().formfield( **{ "coerce": self.to_python, **kwargs, }, ) django-choices-field-4.0.0/django_choices_field/fields.pyi000066400000000000000000000162631512374361600235640ustar00rootroot00000000000000from collections.abc import Callable, Iterable from typing import ( Any, Generic, Literal, TypeAlias, TypeVar, overload, ) from django.db.models import Field, IntegerChoices, TextChoices from django.utils.functional import Promise from django_choices_field.types import IntegerChoicesFlag StrOrPromise: TypeAlias = str | Promise _ValidatorCallable: TypeAlias = Callable[..., None] _ErrorMessagesToOverride: TypeAlias = dict[str, Any] _C = TypeVar("_C", bound=TextChoices | None) class TextChoicesField(Field[_C, _C], Generic[_C]): choices_enum: type[_C] @overload def __new__( cls, choices_enum: type[_C], verbose_name: StrOrPromise | None = ..., name: str | None = ..., primary_key: bool = ..., max_length: int | None = ..., unique: bool = ..., blank: bool = ..., null: Literal[False] = ..., db_index: bool = ..., default: _C | Callable[[], _C] = ..., editable: bool = ..., auto_created: bool = ..., serialize: bool = ..., unique_for_date: str | None = ..., unique_for_month: str | None = ..., unique_for_year: str | None = ..., help_text: StrOrPromise = ..., db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., error_messages: _ErrorMessagesToOverride | None = ..., path: str | Callable[..., str] = ..., match: str | None = ..., recursive: bool = ..., allow_files: bool = ..., allow_folders: bool = ..., ) -> TextChoicesField[_C]: ... @overload def __new__( cls, choices_enum: type[_C], verbose_name: StrOrPromise | None = ..., name: str | None = ..., primary_key: bool = ..., max_length: int | None = ..., unique: bool = ..., blank: bool = ..., null: Literal[True] = ..., db_index: bool = ..., default: _C | Callable[[], _C] | None = ..., editable: bool = ..., auto_created: bool = ..., serialize: bool = ..., unique_for_date: str | None = ..., unique_for_month: str | None = ..., unique_for_year: str | None = ..., help_text: StrOrPromise = ..., db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., error_messages: _ErrorMessagesToOverride | None = ..., path: str | Callable[..., str] = ..., match: str | None = ..., recursive: bool = ..., allow_files: bool = ..., allow_folders: bool = ..., ) -> TextChoicesField[_C | None]: ... _I = TypeVar("_I", bound=IntegerChoices | None) class IntegerChoicesField(Field[_I, _I], Generic[_I]): choices_enum: type[_I] @overload def __new__( cls, choices_enum: type[_I], verbose_name: StrOrPromise | None = ..., name: str | None = ..., primary_key: bool = ..., max_length: int | None = ..., unique: bool = ..., blank: bool = ..., null: Literal[False] = ..., db_index: bool = ..., default: _I | Callable[[], _I] = ..., editable: bool = ..., auto_created: bool = ..., serialize: bool = ..., unique_for_date: str | None = ..., unique_for_month: str | None = ..., unique_for_year: str | None = ..., help_text: StrOrPromise = ..., db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., error_messages: _ErrorMessagesToOverride | None = ..., path: str | Callable[..., str] = ..., match: str | None = ..., recursive: bool = ..., allow_files: bool = ..., allow_folders: bool = ..., ) -> IntegerChoicesField[_I]: ... @overload def __new__( cls, choices_enum: type[_I], verbose_name: StrOrPromise | None = ..., name: str | None = ..., primary_key: bool = ..., max_length: int | None = ..., unique: bool = ..., blank: bool = ..., null: Literal[True] = ..., db_index: bool = ..., default: _I | Callable[[], _I] | None = ..., editable: bool = ..., auto_created: bool = ..., serialize: bool = ..., unique_for_date: str | None = ..., unique_for_month: str | None = ..., unique_for_year: str | None = ..., help_text: StrOrPromise = ..., db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., error_messages: _ErrorMessagesToOverride | None = ..., path: str | Callable[..., str] = ..., match: str | None = ..., recursive: bool = ..., allow_files: bool = ..., allow_folders: bool = ..., ) -> IntegerChoicesField[_I | None]: ... _IF = TypeVar("_IF", bound=IntegerChoicesFlag | None) class IntegerChoicesFlagField(Field[_IF, _IF], Generic[_IF]): choices_enum: type[_IF] @overload def __new__( cls, choices_enum: type[_IF], verbose_name: StrOrPromise | None = ..., name: str | None = ..., primary_key: bool = ..., max_length: int | None = ..., unique: bool = ..., blank: bool = ..., null: Literal[False] = ..., db_index: bool = ..., default: _IF | Callable[[], _IF] = ..., editable: bool = ..., auto_created: bool = ..., serialize: bool = ..., unique_for_date: str | None = ..., unique_for_month: str | None = ..., unique_for_year: str | None = ..., help_text: StrOrPromise = ..., db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., error_messages: _ErrorMessagesToOverride | None = ..., path: str | Callable[..., str] = ..., match: str | None = ..., recursive: bool = ..., allow_files: bool = ..., allow_folders: bool = ..., ) -> IntegerChoicesFlagField[_IF]: ... @overload def __new__( cls, choices_enum: type[_IF], verbose_name: StrOrPromise | None = ..., name: str | None = ..., primary_key: bool = ..., max_length: int | None = ..., unique: bool = ..., blank: bool = ..., null: Literal[True] = ..., db_index: bool = ..., default: _IF | Callable[[], _IF] | None = ..., editable: bool = ..., auto_created: bool = ..., serialize: bool = ..., unique_for_date: str | None = ..., unique_for_month: str | None = ..., unique_for_year: str | None = ..., help_text: StrOrPromise = ..., db_column: str | None = ..., db_tablespace: str | None = ..., validators: Iterable[_ValidatorCallable] = ..., error_messages: _ErrorMessagesToOverride | None = ..., path: str | Callable[..., str] = ..., match: str | None = ..., recursive: bool = ..., allow_files: bool = ..., allow_folders: bool = ..., ) -> IntegerChoicesFlagField[_IF | None]: ... django-choices-field-4.0.0/django_choices_field/py.typed000066400000000000000000000000001512374361600232500ustar00rootroot00000000000000django-choices-field-4.0.0/django_choices_field/types.py000066400000000000000000000011301512374361600232740ustar00rootroot00000000000000import enum from typing import TYPE_CHECKING from django.db import models from typing_extensions import Self if TYPE_CHECKING: import sys class IntegerChoicesFlag(models.IntegerChoices, enum.Flag): """Enumerated integer choices.""" if TYPE_CHECKING: def __or__(self, other: Self) -> Self: ... def __and__(self, other: Self) -> Self: ... def __xor__(self, other: Self) -> Self: ... def __invert__(self) -> Self: ... if sys.version_info >= (3, 11): __ror__ = __or__ __rand__ = __and__ __rxor__ = __xor__ django-choices-field-4.0.0/poetry.lock000066400000000000000000002434431512374361600176670ustar00rootroot00000000000000# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "asgiref" version = "3.11.0" description = "ASGI specs, helper code, and adapters" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ {file = "asgiref-3.11.0-py3-none-any.whl", hash = "sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d"}, {file = "asgiref-3.11.0.tar.gz", hash = "sha256:13acff32519542a1736223fb79a715acdebe24286d98e8b164a73085f40da2c4"}, ] [package.dependencies] typing_extensions = {version = ">=4", markers = "python_version < \"3.11\""} [package.extras] tests = ["mypy (>=1.14.0)", "pytest", "pytest-asyncio"] [[package]] name = "asttokens" version = "3.0.1" description = "Annotate AST trees with source code positions" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a"}, {file = "asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7"}, ] [package.extras] astroid = ["astroid (>=2,<5)"] test = ["astroid (>=2,<5)", "pytest (<9.0)", "pytest-cov", "pytest-xdist"] [[package]] name = "certifi" version = "2025.11.12" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, ] [[package]] name = "charset-normalizer" version = "3.4.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, ] [[package]] name = "codecov" version = "2.1.13" description = "Hosted coverage reports for GitHub, Bitbucket and Gitlab" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["dev"] files = [ {file = "codecov-2.1.13-py2.py3-none-any.whl", hash = "sha256:c2ca5e51bba9ebb43644c43d0690148a55086f7f5e6fd36170858fa4206744d5"}, {file = "codecov-2.1.13.tar.gz", hash = "sha256:2362b685633caeaf45b9951a9b76ce359cd3581dd515b430c6c3f5dfb4d92a8c"}, ] [package.dependencies] coverage = "*" requests = ">=2.7.9" [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.13.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ {file = "coverage-7.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02d9fb9eccd48f6843c98a37bd6817462f130b86da8660461e8f5e54d4c06070"}, {file = "coverage-7.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:367449cf07d33dc216c083f2036bb7d976c6e4903ab31be400ad74ad9f85ce98"}, {file = "coverage-7.13.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cdb3c9f8fef0a954c632f64328a3935988d33a6604ce4bf67ec3e39670f12ae5"}, {file = "coverage-7.13.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d10fd186aac2316f9bbb46ef91977f9d394ded67050ad6d84d94ed6ea2e8e54e"}, {file = "coverage-7.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f88ae3e69df2ab62fb0bc5219a597cb890ba5c438190ffa87490b315190bb33"}, {file = "coverage-7.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4be718e51e86f553bcf515305a158a1cd180d23b72f07ae76d6017c3cc5d791"}, {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a00d3a393207ae12f7c49bb1c113190883b500f48979abb118d8b72b8c95c032"}, {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a7b1cd820e1b6116f92c6128f1188e7afe421c7e1b35fa9836b11444e53ebd9"}, {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:37eee4e552a65866f15dedd917d5e5f3d59805994260720821e2c1b51ac3248f"}, {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62d7c4f13102148c78d7353c6052af6d899a7f6df66a32bddcc0c0eb7c5326f8"}, {file = "coverage-7.13.0-cp310-cp310-win32.whl", hash = "sha256:24e4e56304fdb56f96f80eabf840eab043b3afea9348b88be680ec5986780a0f"}, {file = "coverage-7.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:74c136e4093627cf04b26a35dab8cbfc9b37c647f0502fc313376e11726ba303"}, {file = "coverage-7.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0dfa3855031070058add1a59fdfda0192fd3e8f97e7c81de0596c145dea51820"}, {file = "coverage-7.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fdb6f54f38e334db97f72fa0c701e66d8479af0bc3f9bfb5b90f1c30f54500f"}, {file = "coverage-7.13.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7e442c013447d1d8d195be62852270b78b6e255b79b8675bad8479641e21fd96"}, {file = "coverage-7.13.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ed5630d946859de835a85e9a43b721123a8a44ec26e2830b296d478c7fd4259"}, {file = "coverage-7.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f15a931a668e58087bc39d05d2b4bf4b14ff2875b49c994bbdb1c2217a8daeb"}, {file = "coverage-7.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30a3a201a127ea57f7e14ba43c93c9c4be8b7d17a26e03bb49e6966d019eede9"}, {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a485ff48fbd231efa32d58f479befce52dcb6bfb2a88bb7bf9a0b89b1bc8030"}, {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:22486cdafba4f9e471c816a2a5745337742a617fef68e890d8baf9f3036d7833"}, {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:263c3dbccc78e2e331e59e90115941b5f53e85cfcc6b3b2fbff1fd4e3d2c6ea8"}, {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5330fa0cc1f5c3c4c3bb8e101b742025933e7848989370a1d4c8c5e401ea753"}, {file = "coverage-7.13.0-cp311-cp311-win32.whl", hash = "sha256:0f4872f5d6c54419c94c25dd6ae1d015deeb337d06e448cd890a1e89a8ee7f3b"}, {file = "coverage-7.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51a202e0f80f241ccb68e3e26e19ab5b3bf0f813314f2c967642f13ebcf1ddfe"}, {file = "coverage-7.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:d2a9d7f1c11487b1c69367ab3ac2d81b9b3721f097aa409a3191c3e90f8f3dd7"}, {file = "coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf"}, {file = "coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f"}, {file = "coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb"}, {file = "coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621"}, {file = "coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74"}, {file = "coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57"}, {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8"}, {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d"}, {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b"}, {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd"}, {file = "coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef"}, {file = "coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae"}, {file = "coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080"}, {file = "coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf"}, {file = "coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a"}, {file = "coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74"}, {file = "coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6"}, {file = "coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b"}, {file = "coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232"}, {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971"}, {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d"}, {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137"}, {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511"}, {file = "coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1"}, {file = "coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a"}, {file = "coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6"}, {file = "coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a"}, {file = "coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8"}, {file = "coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053"}, {file = "coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071"}, {file = "coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e"}, {file = "coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493"}, {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0"}, {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e"}, {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c"}, {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e"}, {file = "coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46"}, {file = "coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39"}, {file = "coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e"}, {file = "coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256"}, {file = "coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a"}, {file = "coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9"}, {file = "coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19"}, {file = "coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be"}, {file = "coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb"}, {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8"}, {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b"}, {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9"}, {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927"}, {file = "coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f"}, {file = "coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc"}, {file = "coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b"}, {file = "coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28"}, {file = "coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe"}, {file = "coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657"}, {file = "coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff"}, {file = "coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3"}, {file = "coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b"}, {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d"}, {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e"}, {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940"}, {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2"}, {file = "coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7"}, {file = "coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc"}, {file = "coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a"}, {file = "coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904"}, {file = "coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "django" version = "4.2.27" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ {file = "django-4.2.27-py3-none-any.whl", hash = "sha256:f393a394053713e7d213984555c5b7d3caeee78b2ccb729888a0774dff6c11a8"}, {file = "django-4.2.27.tar.gz", hash = "sha256:b865fbe0f4a3d1ee36594c5efa42b20db3c8bbb10dff0736face1c6e4bda5b92"}, ] [package.dependencies] asgiref = ">=3.6.0,<4" sqlparse = ">=0.3.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] argon2 = ["argon2-cffi (>=19.1.0)"] bcrypt = ["bcrypt"] [[package]] name = "django-types" version = "0.22.0" description = "Type stubs for Django" optional = false python-versions = "<4.0,>=3.10" groups = ["dev"] files = [ {file = "django_types-0.22.0-py3-none-any.whl", hash = "sha256:ba15c756c7a732e58afd0737e54489f1c5e6f1bd24132e9199c637b1f88b057c"}, {file = "django_types-0.22.0.tar.gz", hash = "sha256:4cecc9eee846e7ff2a398bec9dfe6543e76efb922a7a58c5d6064bcb0e6a3dc5"}, ] [package.dependencies] types-psycopg2 = ">=2.9.21.13" [[package]] name = "exceptiongroup" version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev"] markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] [package.dependencies] typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} [package.extras] test = ["pytest (>=6)"] [[package]] name = "executing" version = "2.2.1" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"}, {file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"}, ] [package.extras] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] [[package]] name = "idna" version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, ] [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "iniconfig" version = "2.3.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] [[package]] name = "inline-snapshot" version = "0.31.1" description = "golden master/snapshot/approval testing library which puts the values right into your source code" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "inline_snapshot-0.31.1-py3-none-any.whl", hash = "sha256:7875a73c986a03388c7e758fb5cb8a43d2c3a20328aa1d851bfb4ed536c4496f"}, {file = "inline_snapshot-0.31.1.tar.gz", hash = "sha256:4ea5ed70aa1d652713bbfd750606b94bd8a42483f7d3680433b3e92994495f64"}, ] [package.dependencies] asttokens = ">=2.0.5" executing = ">=2.2.0" pytest = ">=8.3.4" rich = ">=13.7.1" tomli = {version = ">=2.0.0", markers = "python_version < \"3.11\""} [package.extras] black = ["black (>=23.3.0)"] dirty-equals = ["dirty-equals (>=0.9.0)"] [[package]] name = "librt" version = "0.7.5" description = "Mypyc runtime library" optional = false python-versions = ">=3.9" groups = ["dev"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "librt-0.7.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81056e01bba1394f1d92904ec61a4078f66df785316275edbaf51d90da8c6e26"}, {file = "librt-0.7.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7c72c8756eeb3aefb1b9e3dac7c37a4a25db63640cac0ab6fc18e91a0edf05a"}, {file = "librt-0.7.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ddc4a16207f88f9597b397fc1f60781266d13b13de922ff61c206547a29e4bbd"}, {file = "librt-0.7.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63055d3dda433ebb314c9f1819942f16a19203c454508fdb2d167613f7017169"}, {file = "librt-0.7.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f85f9b5db87b0f52e53c68ad2a0c5a53e00afa439bd54a1723742a2b1021276"}, {file = "librt-0.7.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c566a4672564c5d54d8ab65cdaae5a87ee14c1564c1a2ddc7a9f5811c750f023"}, {file = "librt-0.7.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fee15c2a190ef389f14928135c6fb2d25cd3fdb7887bfd9a7b444bbdc8c06b96"}, {file = "librt-0.7.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:584cb3e605ec45ba350962cec853e17be0a25a772f21f09f1e422f7044ae2a7d"}, {file = "librt-0.7.5-cp310-cp310-win32.whl", hash = "sha256:9c08527055fbb03c641c15bbc5b79dd2942fb6a3bd8dabf141dd7e97eeea4904"}, {file = "librt-0.7.5-cp310-cp310-win_amd64.whl", hash = "sha256:dd810f2d39c526c42ea205e0addad5dc08ef853c625387806a29d07f9d150d9b"}, {file = "librt-0.7.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f952e1a78c480edee8fb43aa2bf2e84dcd46c917d44f8065b883079d3893e8fc"}, {file = "librt-0.7.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75965c1f4efb7234ff52a58b729d245a21e87e4b6a26a0ec08052f02b16274e4"}, {file = "librt-0.7.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:732e0aa0385b59a1b2545159e781c792cc58ce9c134249233a7c7250a44684c4"}, {file = "librt-0.7.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdde31759bd8888f3ef0eebda80394a48961328a17c264dce8cc35f4b9cde35d"}, {file = "librt-0.7.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3146d52465b3b6397d25d513f428cb421c18df65b7378667bb5f1e3cc45805"}, {file = "librt-0.7.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29c8d2fae11d4379ea207ba7fc69d43237e42cf8a9f90ec6e05993687e6d648b"}, {file = "librt-0.7.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb41f04046b4f22b1e7ba5ef513402cd2e3477ec610e5f92d38fe2bba383d419"}, {file = "librt-0.7.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8bb7883c1e94ceb87c2bf81385266f032da09cd040e804cc002f2c9d6b842e2f"}, {file = "librt-0.7.5-cp311-cp311-win32.whl", hash = "sha256:84d4a6b9efd6124f728558a18e79e7cc5c5d4efc09b2b846c910de7e564f5bad"}, {file = "librt-0.7.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab4b0d3bee6f6ff7017e18e576ac7e41a06697d8dea4b8f3ab9e0c8e1300c409"}, {file = "librt-0.7.5-cp311-cp311-win_arm64.whl", hash = "sha256:730be847daad773a3c898943cf67fb9845a3961d06fb79672ceb0a8cd8624cfa"}, {file = "librt-0.7.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba1077c562a046208a2dc6366227b3eeae8f2c2ab4b41eaf4fd2fa28cece4203"}, {file = "librt-0.7.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:654fdc971c76348a73af5240d8e2529265b9a7ba6321e38dd5bae7b0d4ab3abe"}, {file = "librt-0.7.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6b7b58913d475911f6f33e8082f19dd9b120c4f4a5c911d07e395d67b81c6982"}, {file = "librt-0.7.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8e0fd344bad57026a8f4ccfaf406486c2fc991838050c2fef156170edc3b775"}, {file = "librt-0.7.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46aa91813c267c3f60db75d56419b42c0c0b9748ec2c568a0e3588e543fb4233"}, {file = "librt-0.7.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ddc0ab9dbc5f9ceaf2bf7a367bf01f2697660e908f6534800e88f43590b271db"}, {file = "librt-0.7.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7a488908a470451338607650f1c064175094aedebf4a4fa37890682e30ce0b57"}, {file = "librt-0.7.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e47fc52602ffc374e69bf1b76536dc99f7f6dd876bd786c8213eaa3598be030a"}, {file = "librt-0.7.5-cp312-cp312-win32.whl", hash = "sha256:cda8b025875946ffff5a9a7590bf9acde3eb02cb6200f06a2d3e691ef3d9955b"}, {file = "librt-0.7.5-cp312-cp312-win_amd64.whl", hash = "sha256:b591c094afd0ffda820e931148c9e48dc31a556dc5b2b9b3cc552fa710d858e4"}, {file = "librt-0.7.5-cp312-cp312-win_arm64.whl", hash = "sha256:532ddc6a8a6ca341b1cd7f4d999043e4c71a212b26fe9fd2e7f1e8bb4e873544"}, {file = "librt-0.7.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b1795c4b2789b458fa290059062c2f5a297ddb28c31e704d27e161386469691a"}, {file = "librt-0.7.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2fcbf2e135c11f721193aa5f42ba112bb1046afafbffd407cbc81d8d735c74d0"}, {file = "librt-0.7.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c039bbf79a9a2498404d1ae7e29a6c175e63678d7a54013a97397c40aee026c5"}, {file = "librt-0.7.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3919c9407faeeee35430ae135e3a78acd4ecaaaa73767529e2c15ca1d73ba325"}, {file = "librt-0.7.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26b46620e1e0e45af510d9848ea0915e7040605dd2ae94ebefb6c962cbb6f7ec"}, {file = "librt-0.7.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9bbb8facc5375476d392990dd6a71f97e4cb42e2ac66f32e860f6e47299d5e89"}, {file = "librt-0.7.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e9e9c988b5ffde7be02180f864cbd17c0b0c1231c235748912ab2afa05789c25"}, {file = "librt-0.7.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:edf6b465306215b19dbe6c3fb63cf374a8f3e1ad77f3b4c16544b83033bbb67b"}, {file = "librt-0.7.5-cp313-cp313-win32.whl", hash = "sha256:060bde69c3604f694bd8ae21a780fe8be46bb3dbb863642e8dfc75c931ca8eee"}, {file = "librt-0.7.5-cp313-cp313-win_amd64.whl", hash = "sha256:a82d5a0ee43aeae2116d7292c77cc8038f4841830ade8aa922e098933b468b9e"}, {file = "librt-0.7.5-cp313-cp313-win_arm64.whl", hash = "sha256:3c98a8d0ac9e2a7cb8ff8c53e5d6e8d82bfb2839abf144fdeaaa832f2a12aa45"}, {file = "librt-0.7.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9937574e6d842f359b8585903d04f5b4ab62277a091a93e02058158074dc52f2"}, {file = "librt-0.7.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5cd3afd71e9bc146203b6c8141921e738364158d4aa7cdb9a874e2505163770f"}, {file = "librt-0.7.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9cffa3ef0af29687455161cb446eff059bf27607f95163d6a37e27bcb37180f6"}, {file = "librt-0.7.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82f3f088482e2229387eadf8215c03f7726d56f69cce8c0c40f0795aebc9b361"}, {file = "librt-0.7.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7aa33153a5bb0bac783d2c57885889b1162823384e8313d47800a0e10d0070e"}, {file = "librt-0.7.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:265729b551a2dd329cc47b323a182fb7961af42abf21e913c9dd7d3331b2f3c2"}, {file = "librt-0.7.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:168e04663e126416ba712114050f413ac306759a1791d87b7c11d4428ba75760"}, {file = "librt-0.7.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:553dc58987d1d853adda8aeadf4db8e29749f0b11877afcc429a9ad892818ae2"}, {file = "librt-0.7.5-cp314-cp314-win32.whl", hash = "sha256:263f4fae9eba277513357c871275b18d14de93fd49bf5e43dc60a97b81ad5eb8"}, {file = "librt-0.7.5-cp314-cp314-win_amd64.whl", hash = "sha256:85f485b7471571e99fab4f44eeb327dc0e1f814ada575f3fa85e698417d8a54e"}, {file = "librt-0.7.5-cp314-cp314-win_arm64.whl", hash = "sha256:49c596cd18e90e58b7caa4d7ca7606049c1802125fcff96b8af73fa5c3870e4d"}, {file = "librt-0.7.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:54d2aef0b0f5056f130981ad45081b278602ff3657fe16c88529f5058038e802"}, {file = "librt-0.7.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0b4791202296ad51ac09a3ff58eb49d9da8e3a4009167a6d76ac418a974e5fd4"}, {file = "librt-0.7.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e860909fea75baef941ee6436e0453612505883b9d0d87924d4fda27865b9a2"}, {file = "librt-0.7.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f02c4337bf271c4f06637f5ff254fad2238c0b8e32a3a480ebb2fc5e26f754a5"}, {file = "librt-0.7.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7f51ffe59f4556243d3cc82d827bde74765f594fa3ceb80ec4de0c13ccd3416"}, {file = "librt-0.7.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0b7f080ba30601dfa3e3deed3160352273e1b9bc92e652f51103c3e9298f7899"}, {file = "librt-0.7.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fb565b4219abc8ea2402e61c7ba648a62903831059ed3564fa1245cc245d58d7"}, {file = "librt-0.7.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a3cfb15961e7333ea6ef033dc574af75153b5c230d5ad25fbcd55198f21e0cf"}, {file = "librt-0.7.5-cp314-cp314t-win32.whl", hash = "sha256:118716de5ad6726332db1801bc90fa6d94194cd2e07c1a7822cebf12c496714d"}, {file = "librt-0.7.5-cp314-cp314t-win_amd64.whl", hash = "sha256:3dd58f7ce20360c6ce0c04f7bd9081c7f9c19fc6129a3c705d0c5a35439f201d"}, {file = "librt-0.7.5-cp314-cp314t-win_arm64.whl", hash = "sha256:08153ea537609d11f774d2bfe84af39d50d5c9ca3a4d061d946e0c9d8bce04a1"}, {file = "librt-0.7.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:df2e210400b28e50994477ebf82f055698c79797b6ee47a1669d383ca33263e1"}, {file = "librt-0.7.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d2cc7d187e8c6e9b7bdbefa9697ce897a704ea7a7ce844f2b4e0e2aa07ae51d3"}, {file = "librt-0.7.5-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39183abee670bc37b85f11e86c44a9cad1ed6efa48b580083e89ecee13dd9717"}, {file = "librt-0.7.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191cbd42660446d67cf7a95ac7bfa60f49b8b3b0417c64f216284a1d86fc9335"}, {file = "librt-0.7.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea1b60b86595a5dc1f57b44a801a1c4d8209c0a69518391d349973a4491408e6"}, {file = "librt-0.7.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:af69d9e159575e877c7546d1ee817b4ae089aa221dd1117e20c24ad8dc8659c7"}, {file = "librt-0.7.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0e2bf8f91093fac43e3eaebacf777f12fd539dce9ec5af3efc6d8424e96ccd49"}, {file = "librt-0.7.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8dcae24de1bc9da93aa689cb6313c70e776d7cea2fcf26b9b6160fedfe6bd9af"}, {file = "librt-0.7.5-cp39-cp39-win32.whl", hash = "sha256:cdb001a1a0e4f41e613bca2c0fc147fc8a7396f53fc94201cbfd8ec7cd69ca4b"}, {file = "librt-0.7.5-cp39-cp39-win_amd64.whl", hash = "sha256:a9eacbf983319b26b5f340a2e0cd47ac1ee4725a7f3a72fd0f15063c934b69d6"}, {file = "librt-0.7.5.tar.gz", hash = "sha256:de4221a1181fa9c8c4b5f35506ed6f298948f44003d84d2a8b9885d7e01e6cfa"}, ] [[package]] name = "markdown-it-py" version = "4.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, ] [package.dependencies] mdurl = ">=0.1,<1.0" [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins (>=0.5.0)"] profiling = ["gprof2dot"] rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] [[package]] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] [[package]] name = "mypy" version = "1.19.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, ] [package.dependencies] librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""} mypy_extensions = ">=1.0.0" pathspec = ">=0.9.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing_extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] name = "nodeenv" version = "1.10.0" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] files = [ {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"}, {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"}, ] [[package]] name = "packaging" version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "pluggy" version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "pygments" version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyright" version = "1.1.407" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" groups = ["dev"] files = [ {file = "pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21"}, {file = "pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262"}, ] [package.dependencies] nodeenv = ">=1.6.0" typing-extensions = ">=4.1" [package.extras] all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] nodejs = ["nodejs-wheel-binaries"] [[package]] name = "pytest" version = "9.0.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ {file = "pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b"}, {file = "pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11"}, ] [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} iniconfig = ">=1.0.1" packaging = ">=22" pluggy = ">=1.5,<2" pygments = ">=2.7.2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" version = "7.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, ] [package.dependencies] coverage = {version = ">=7.10.6", extras = ["toml"]} pluggy = ">=1.2" pytest = ">=7" [package.extras] testing = ["process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-django" version = "4.11.1" description = "A Django plugin for pytest." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "pytest_django-4.11.1-py3-none-any.whl", hash = "sha256:1b63773f648aa3d8541000c26929c1ea63934be1cfa674c76436966d73fe6a10"}, {file = "pytest_django-4.11.1.tar.gz", hash = "sha256:a949141a1ee103cb0e7a20f1451d355f83f5e4a5d07bdd4dcfdd1fd0ff227991"}, ] [package.dependencies] pytest = ">=7.0.0" [package.extras] docs = ["sphinx", "sphinx_rtd_theme"] testing = ["Django", "django-configurations (>=2.0)"] [[package]] name = "requests" version = "2.32.5" description = "Python HTTP for Humans." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] certifi = ">=2017.4.17" charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" version = "14.2.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" groups = ["dev"] files = [ {file = "rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd"}, {file = "rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" version = "0.14.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ {file = "ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49"}, {file = "ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f"}, {file = "ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d"}, {file = "ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77"}, {file = "ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a"}, {file = "ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f"}, {file = "ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935"}, {file = "ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e"}, {file = "ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d"}, {file = "ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f"}, {file = "ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f"}, {file = "ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d"}, {file = "ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405"}, {file = "ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60"}, {file = "ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830"}, {file = "ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6"}, {file = "ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154"}, {file = "ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6"}, {file = "ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4"}, ] [[package]] name = "sqlparse" version = "0.5.5" description = "A non-validating SQL parser." optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ {file = "sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba"}, {file = "sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e"}, ] [package.extras] dev = ["build"] doc = ["sphinx"] [[package]] name = "tomli" version = "2.3.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] markers = "python_full_version <= \"3.11.0a6\"" files = [ {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, ] [[package]] name = "ty" version = "0.0.7" description = "An extremely fast Python type checker, written in Rust." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ {file = "ty-0.0.7-py3-none-linux_armv6l.whl", hash = "sha256:b30105bd9a0b064497111c50c206d5b6a032f29bcf39f09a12085c3009d72784"}, {file = "ty-0.0.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b4df20889115f3d5611a9d9cdedc222e3fd82b5fe87bb0a9f7246e53a23becc7"}, {file = "ty-0.0.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f699589d8511e1e17c5a7edfc5f4a4e80f2a6d4a3932a0e9e3422fd32d731472"}, {file = "ty-0.0.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3eaec2d8aa153ee4bcc43b17a384d0f9e66177c8c8127be3358b6b8348b9e3b"}, {file = "ty-0.0.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:177d160295e6a56bdf0b61f6120bc4502fff301d4d10855ba711c109aa7f37fb"}, {file = "ty-0.0.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30518b95ab5cc83615794cca765a5fb86df39a0d9c3dadc0ab2d787ab7830008"}, {file = "ty-0.0.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7867b3f75c2d9602cc6fb3b6d462580b707c2d112d4b27037142b0d01f8bfd03"}, {file = "ty-0.0.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878d45858e209b7904753fbc5155f4cb75dadc20a26bbb77614bfef31580f9ae"}, {file = "ty-0.0.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:651820b193901825afce40ae68f6a51cd64dbfa4b81a45db90061401261f25e4"}, {file = "ty-0.0.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f56a5a0c1c045863b1b70c358a392b3f73b8528c5c571d409f19dd465525e116"}, {file = "ty-0.0.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:748218fbc1f7b7f1b9d14e77d4f3d7fec72af794417e26b0185bdb94153afe1c"}, {file = "ty-0.0.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1ff80f3985a52a7358b9069b4a8d223e92cf312544a934a062d6d3a4fb6876b3"}, {file = "ty-0.0.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a808910ce672ba4446699f4c021283208f58f988bcfc3bdbdfc6e005819d9ee0"}, {file = "ty-0.0.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2718fea5f314eda01703fb406ec89b1fc8710b3fc6a09bbd6f7a4f3502ddc889"}, {file = "ty-0.0.7-py3-none-win32.whl", hash = "sha256:ae89bb8dc50deb66f34eab3113aa61ac5d7f85ecf16279e5918548085a89021c"}, {file = "ty-0.0.7-py3-none-win_amd64.whl", hash = "sha256:25bd20e3d4d0f07b422f9b42711ba24d28116031273bd23dbda66cec14df1c06"}, {file = "ty-0.0.7-py3-none-win_arm64.whl", hash = "sha256:c87d27484dba9fca0053b6a9eee47eecc760aab2bbb8e6eab3d7f81531d1ad0c"}, {file = "ty-0.0.7.tar.gz", hash = "sha256:90e53b20b86c418ee41a8385f17da44cc7f916f96f9eee87593423ce8292ca72"}, ] [[package]] name = "types-psycopg2" version = "2.9.21.20251012" description = "Typing stubs for psycopg2" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "types_psycopg2-2.9.21.20251012-py3-none-any.whl", hash = "sha256:712bad5c423fe979e357edbf40a07ca40ef775d74043de72bd4544ca328cc57e"}, {file = "types_psycopg2-2.9.21.20251012.tar.gz", hash = "sha256:4cdafd38927da0cfde49804f39ab85afd9c6e9c492800e42f1f0c1a1b0312935"}, ] [[package]] name = "typing-extensions" version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] [[package]] name = "tzdata" version = "2025.3" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["main", "dev"] markers = "sys_platform == \"win32\"" files = [ {file = "tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1"}, {file = "tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7"}, ] [[package]] name = "urllib3" version = "2.6.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"}, {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"}, ] [package.extras] brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" content-hash = "f1fe1e4763479930168b1cdfa419ad4c34b80b49d0a81857c84d4a0750e397d6" django-choices-field-4.0.0/pyproject.toml000066400000000000000000000074021512374361600204000ustar00rootroot00000000000000[tool.poetry] name = "django-choices-field" version = "4.0.0" description = "Django field that set/get django's new TextChoices/IntegerChoices enum." authors = ["Thiago Bellini Ribeiro "] license = "MIT" readme = "README.md" homepage = "https://github.com/bellini666/django-choices-field" repository = "https://github.com/bellini666/django-choices-field" documentation = "https://django-choices-field.readthedocs.io" keywords = ["django", "enum"] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Framework :: Django", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", "Framework :: Django :: 5.2", "Framework :: Django :: 6.0", ] packages = [{ include = "django_choices_field" }] [tool.poetry.dependencies] python = ">=3.10,<4.0" django = ">=4.2" typing_extensions = ">=4.0.0" [tool.poetry.group.dev.dependencies] codecov = "^2.1.11" django = "^4.2" django-types = "^0.22.0" inline-snapshot = "^0.31.1" mypy = "^1.16.0" pyright = "^1.1.400" pytest = "^9.0.2" pytest-cov = "^7.0.0" pytest-django = "^4.2.0" ruff = "^0.14.0" ty = "^0.0.7" [tool.ruff] target-version = "py310" line-length = 100 [tool.ruff.lint] extend-select = [ "A", "ASYNC", "B", "BLE", "C4", "COM", "D", "D2", "D3", "D4", "DTZ", "E", "ERA", "EXE", "F", "FURB", "G", "I", "ICN001", "INP", "ISC", "N", "PERF", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "Q", "RET", "RSE", "RUF", "SIM", "SLF", "SLOT", "T10", "T20", "TCH", "TID", "TRY", "UP", "W", "YTT", ] extend-ignore = [ "D100", "D102", "D104", "D105", "D107", "TRY003", # ruff formatter recommends to disable those "COM812", "COM819", "D206", "E111", "E114", "E117", "E501", "ISC001", "Q000", "Q001", "Q002", "Q003", "W191", ] exclude = [ ".eggs", ".git", ".hg", ".mypy_cache", ".tox", ".venv", "__pycached__", "_build", "buck-out", "build", "dist", ] [tool.ruff.lint.per-file-ignores] "tests/*" = ["A003", "PLW0603", "PLR2004", "D", "PGH003", "SLF001"] "examples/*" = ["A003"] "**/migrations/*" = ["RUF012"] [tool.ruff.lint.pydocstyle] convention = "google" [tool.ruff.lint.isort] [tool.ruff.format] [tool.pyright] pythonVersion = "3.10" useLibraryCodeForTypes = true ignore = ["**/migrations", "tests"] reportCallInDefaultInitializer = "warning" reportMatchNotExhaustive = "warning" reportMissingSuperCall = "warning" reportOverlappingOverload = "warning" reportUninitializedInstanceVariable = "none" reportUnnecessaryCast = "warning" reportUnnecessaryTypeIgnoreComment = "warning" reportUntypedNamedTuple = "error" reportUnusedExpression = "warning" reportUnnecessaryComparison = "warning" reportUnnecessaryContains = "warning" reportIncompatibleVariableOverride = "none" reportIncompatibleMethodOverride = "none" strictDictionaryInference = true strictListInference = true strictSetInference = true [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "tests.settings" python_files = "test_*.py" testpaths = ["tests"] addopts = "--cov=./ -vv" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" django-choices-field-4.0.0/tests/000077500000000000000000000000001512374361600166235ustar00rootroot00000000000000django-choices-field-4.0.0/tests/__init__.py000066400000000000000000000000001512374361600207220ustar00rootroot00000000000000django-choices-field-4.0.0/tests/models.py000066400000000000000000000104671512374361600204700ustar00rootroot00000000000000import enum import sys from django.db import models from django.utils.translation import gettext_lazy as _ from django_choices_field import IntegerChoicesField, TextChoicesField from django_choices_field.fields import IntegerChoicesFlagField from django_choices_field.types import IntegerChoicesFlag class MyModel(models.Model): class TextEnum(models.TextChoices): C_FOO = "foo", "T Foo Description" C_BAR = "bar", "T Bar Description" class TextEnumWithEmptyStateLabel(models.TextChoices): __empty__ = "This is the label for the text empty value" C_FOO = "foo", "T Foo Description" C_BAR = "bar", "T Bar Description" class IntegerEnum(models.IntegerChoices): I_FOO = 1, "I Foo Description" I_BAR = 2, "I Bar Description" class IntegerEnumWithEmptyStateLabel(models.IntegerChoices): __empty__ = "This is the label for the int empty value" I_FOO = 1, "I Foo Description" I_BAR = 2, "I Bar Description" class IntegerFlagEnum(IntegerChoicesFlag): IF_FOO = ( enum.auto() if sys.version_info >= (3, 11) else 1, "IF Foo Description", # type: ignore ) IF_BAR = ( enum.auto() if sys.version_info >= (3, 11) else 2, "IF Bar Description", # type: ignore ) IF_BIN = ( enum.auto() if sys.version_info >= (3, 11) else 4, "IF Bin Description", # type: ignore ) class IntegerFlagEnumTranslated(IntegerChoicesFlag): IF_FOO = ( enum.auto() if sys.version_info >= (3, 11) else 1, _("IF Foo Description"), # type: ignore ) IF_BAR = ( enum.auto() if sys.version_info >= (3, 11) else 2, _("IF Bar Description"), # type: ignore ) IF_BIN = ( enum.auto() if sys.version_info >= (3, 11) else 4, _("IF Bin Description"), # type: ignore ) class IntegerFlagEnumWithEmptyStateLabel(IntegerChoicesFlag): __empty__ = "This is the label for the flag empty value" IF_FOO = ( enum.auto() if sys.version_info >= (3, 11) else 1, "IF Foo Description", # type: ignore ) IF_BAR = ( enum.auto() if sys.version_info >= (3, 11) else 2, "IF Bar Description", # type: ignore ) IF_BIN = ( enum.auto() if sys.version_info >= (3, 11) else 4, "IF Bin Description", # type: ignore ) objects = models.Manager["MyModel"]() c_field = TextChoicesField( choices_enum=TextEnum, default=TextEnum.C_FOO, ) c_field_nullable = TextChoicesField( choices_enum=TextEnum, null=True, ) c_field_with_empty_state = TextChoicesField( choices_enum=TextEnumWithEmptyStateLabel, default=TextEnumWithEmptyStateLabel.C_FOO, ) c_field_with_empty_state_nullable = TextChoicesField( choices_enum=TextEnumWithEmptyStateLabel, null=True, ) i_field = IntegerChoicesField( choices_enum=IntegerEnum, default=IntegerEnum.I_FOO, ) i_field_nullable = IntegerChoicesField( choices_enum=IntegerEnum, null=True, ) i_field_with_empty_state = IntegerChoicesField( choices_enum=IntegerEnumWithEmptyStateLabel, default=IntegerEnumWithEmptyStateLabel.I_FOO, ) i_field_with_empty_state_nullable = IntegerChoicesField( choices_enum=IntegerEnumWithEmptyStateLabel, null=True, ) if_field = IntegerChoicesFlagField( choices_enum=IntegerFlagEnum, default=IntegerFlagEnum.IF_FOO, ) if_field_nullable = IntegerChoicesFlagField( choices_enum=IntegerFlagEnum, null=True, ) ift_field = IntegerChoicesFlagField( choices_enum=IntegerFlagEnumTranslated, default=IntegerFlagEnumTranslated.IF_FOO, ) ift_field_nullable = IntegerChoicesFlagField( choices_enum=IntegerFlagEnumTranslated, null=True, ) if_field_with_empty_state = IntegerChoicesFlagField( choices_enum=IntegerFlagEnumWithEmptyStateLabel, default=IntegerFlagEnumWithEmptyStateLabel.IF_FOO, ) if_field_with_empty_state_nullable = IntegerChoicesFlagField( choices_enum=IntegerFlagEnumWithEmptyStateLabel, null=True, ) django-choices-field-4.0.0/tests/settings.py000066400000000000000000000012631512374361600210370ustar00rootroot00000000000000from django.db import models from django.db.models.manager import BaseManager from django.db.models.query import QuerySet for cls in [QuerySet, BaseManager, models.ForeignKey]: cls.__class_getitem__ = classmethod(lambda cls, *args, **kwargs: cls) # type:ignore USE_TZ = True INSTALLED_APPS = [ "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "tests", ] DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", }, } MIDDLEWARE = [ "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", ] SECRET_KEY = "dummy" ROOT_URLCONF = "tests.urls" django-choices-field-4.0.0/tests/test_fields.py000066400000000000000000000324341512374361600215100ustar00rootroot00000000000000import sys import pytest from django.core.exceptions import ImproperlyConfigured, ValidationError from django.db import models from django_choices_field.fields import ( IntegerChoicesField, IntegerChoicesFlagField, TextChoicesField, ) from django_choices_field.types import IntegerChoicesFlag from .models import MyModel @pytest.mark.parametrize("fname", ["c_field", "c_field_nullable"]) def test_field_choices_text(fname: str): f = MyModel._meta.get_field(fname) assert f.choices == [ ("foo", "T Foo Description"), ("bar", "T Bar Description"), ] def test_field_choices_text_with_labelled_empty_state(): assert MyModel._meta.get_field("c_field_with_empty_state").choices == [ ("foo", "T Foo Description"), ("bar", "T Bar Description"), ] assert MyModel._meta.get_field("c_field_with_empty_state_nullable").choices == [ (None, "This is the label for the text empty value"), ("foo", "T Foo Description"), ("bar", "T Bar Description"), ] @pytest.mark.parametrize("fname", ["i_field", "i_field_nullable"]) def test_field_choices_integer(fname: str): f = MyModel._meta.get_field(fname) assert f.choices == [ (1, "I Foo Description"), (2, "I Bar Description"), ] def test_field_choices_integer_with_labelled_empty_state(): assert MyModel._meta.get_field("i_field_with_empty_state").choices == [ (1, "I Foo Description"), (2, "I Bar Description"), ] assert MyModel._meta.get_field("i_field_with_empty_state_nullable").choices == [ (None, "This is the label for the int empty value"), (1, "I Foo Description"), (2, "I Bar Description"), ] @pytest.mark.parametrize( "fname", ["if_field", "if_field_nullable", "ift_field", "ift_field_nullable"], ) def test_field_choices_integer_flags(fname: str): f = MyModel._meta.get_field(fname) assert f.choices == [ (1, "IF Foo Description"), (2, "IF Bar Description"), (4, "IF Bin Description"), (3, "IF Foo Description|IF Bar Description"), (5, "IF Foo Description|IF Bin Description"), (6, "IF Bar Description|IF Bin Description"), (7, "IF Foo Description|IF Bar Description|IF Bin Description"), ] def test_field_choices_integer_flags_with_empty_state_label(): expected_choices = [ (None, "This is the label for the flag empty value"), (1, "IF Foo Description"), (2, "IF Bar Description"), (4, "IF Bin Description"), (3, "IF Foo Description|IF Bar Description"), (5, "IF Foo Description|IF Bin Description"), (6, "IF Bar Description|IF Bin Description"), (7, "IF Foo Description|IF Bar Description|IF Bin Description"), ] assert MyModel._meta.get_field("if_field_with_empty_state").choices == [ x for x in expected_choices if x[0] ] assert MyModel._meta.get_field("if_field_with_empty_state_nullable").choices == expected_choices def test_default_value_text(): m = MyModel() assert isinstance(m.c_field, MyModel.TextEnum) assert m.c_field == MyModel.TextEnum.C_FOO assert m.c_field_nullable is None def test_default_value_integer(): m = MyModel() assert isinstance(m.i_field, MyModel.IntegerEnum) assert m.i_field == MyModel.IntegerEnum.I_FOO assert m.i_field_nullable is None def test_default_value_integer_flag(): m = MyModel() assert isinstance(m.if_field, MyModel.IntegerFlagEnum) assert m.if_field == MyModel.IntegerFlagEnum.IF_FOO assert m.if_field_nullable is None def test_created_value_text(): m = MyModel(c_field=MyModel.TextEnum.C_BAR) assert isinstance(m.c_field, MyModel.TextEnum) assert m.c_field == MyModel.TextEnum.C_BAR assert m.c_field_nullable is None def test_created_value_integer(): m = MyModel(i_field=MyModel.IntegerEnum.I_BAR) assert isinstance(m.i_field, MyModel.IntegerEnum) assert m.i_field == MyModel.IntegerEnum.I_BAR assert m.i_field_nullable is None def test_created_value_integer_flag(): m = MyModel(if_field=MyModel.IntegerFlagEnum.IF_BAR) assert isinstance(m.if_field, MyModel.IntegerFlagEnum) assert m.if_field == MyModel.IntegerFlagEnum.IF_BAR assert m.if_field_nullable is None @pytest.mark.skipif(sys.version_info < (3, 11), reason="Requires Python 3.11+ to work properly") def test_created_value_integer_flag_multiple(): m = MyModel(if_field=MyModel.IntegerFlagEnum.IF_BAR | MyModel.IntegerFlagEnum.IF_BIN) assert isinstance(m.if_field, MyModel.IntegerFlagEnum) assert m.if_field == MyModel.IntegerFlagEnum.IF_BAR | MyModel.IntegerFlagEnum.IF_BIN assert m.if_field_nullable is None def test_set_value_text(db): m = MyModel() assert isinstance(m.c_field, MyModel.TextEnum) assert m.c_field == MyModel.TextEnum.C_FOO m.c_field = MyModel.TextEnum.C_BAR assert isinstance(m.c_field, MyModel.TextEnum) assert m.c_field == MyModel.TextEnum.C_BAR m.save() def test_set_value_integer(db): m = MyModel() assert isinstance(m.i_field, MyModel.IntegerEnum) assert m.i_field == MyModel.IntegerEnum.I_FOO m.i_field = MyModel.IntegerEnum.I_BAR assert isinstance(m.i_field, MyModel.IntegerEnum) assert m.i_field == MyModel.IntegerEnum.I_BAR m.save() def test_set_value_integer_flag(db): m = MyModel() assert isinstance(m.if_field, MyModel.IntegerFlagEnum) assert m.if_field == MyModel.IntegerFlagEnum.IF_FOO m.if_field = MyModel.IntegerFlagEnum.IF_BAR assert isinstance(m.if_field, MyModel.IntegerFlagEnum) assert m.if_field == MyModel.IntegerFlagEnum.IF_BAR m.save() @pytest.mark.skipif(sys.version_info < (3, 11), reason="Requires Python 3.11+ to work properly") def test_set_value_integer_flag_multiple(db): m = MyModel() assert isinstance(m.if_field, MyModel.IntegerFlagEnum) assert m.if_field == MyModel.IntegerFlagEnum.IF_FOO m.if_field = MyModel.IntegerFlagEnum.IF_BAR | MyModel.IntegerFlagEnum.IF_BIN assert isinstance(m.if_field, MyModel.IntegerFlagEnum) assert m.if_field == MyModel.IntegerFlagEnum.IF_BAR | MyModel.IntegerFlagEnum.IF_BIN m.save() def test_set_none_text(db): m = MyModel(c_field_nullable=MyModel.TextEnum.C_FOO) assert isinstance(m.c_field_nullable, MyModel.TextEnum) assert m.c_field_nullable == MyModel.TextEnum.C_FOO m.c_field_nullable = None m.save() m = MyModel.objects.get(pk=m.pk) assert m.c_field_nullable is None def test_set_none_integer(db): m = MyModel(i_field_nullable=MyModel.IntegerEnum.I_FOO) assert isinstance(m.i_field_nullable, MyModel.IntegerEnum) assert m.i_field_nullable == MyModel.IntegerEnum.I_FOO m.i_field_nullable = None m.save() m = MyModel.objects.get(pk=m.pk) assert m.i_field_nullable is None def test_set_none_integer_flag(db): m = MyModel(if_field_nullable=MyModel.IntegerFlagEnum.IF_FOO) assert isinstance(m.if_field_nullable, MyModel.IntegerFlagEnum) assert m.if_field_nullable == MyModel.IntegerFlagEnum.IF_FOO m.if_field_nullable = None m.save() m = MyModel.objects.get(pk=m.pk) assert m.if_field_nullable is None def test_set_text_integer(db): m = MyModel(i_field_nullable=MyModel.IntegerEnum.I_FOO) assert isinstance(m.i_field_nullable, MyModel.IntegerEnum) assert m.i_field_nullable == MyModel.IntegerEnum.I_FOO m.i_field = "2" # type: ignore m.save() m = MyModel.objects.get(pk=m.pk) assert m.i_field == 2 def test_set_empty_value_text(db): # Passing an empty value should not raise an error m = MyModel() m.c_field_nullable = "" m.save() @pytest.mark.parametrize("v", [10, "abc"]) def test_set_wrong_value_text(v, db): m = MyModel() m.c_field = v with pytest.raises(ValidationError) as exc: m.save() assert list(exc.value) == [f"“{v}” must be a subclass of ."] @pytest.mark.parametrize("v", [10, "abc"]) def test_set_wrong_value_integer(v, db): if isinstance(v, int): m = MyModel() m.i_field = v # type: ignore with pytest.raises(ValidationError) as exc: m.save() assert list(exc.value) == [f"“{v}” must be a subclass of ."] else: m = MyModel() m.i_field = v with pytest.raises(ValueError) as exc: # noqa: PT011 m.save() assert str(exc.value) == f"Field 'i_field' expected a number but got '{v}'." @pytest.mark.parametrize("v", [10, "abc"]) def test_set_wrong_value_integer_flag(v, db): if isinstance(v, int): m = MyModel() m.if_field = v # type: ignore with pytest.raises(ValidationError) as exc: m.save() cname = "flag" if sys.version_info >= (3, 11) else "enum" assert list(exc.value) == [f"“{v}” must be a subclass of <{cname} 'IntegerFlagEnum'>."] else: m = MyModel() m.if_field = v with pytest.raises(ValueError) as exc: # noqa: PT011 m.save() assert str(exc.value) == f"Field 'if_field' expected a number but got '{v}'." def test_text_field_get_display(db): m = MyModel() assert isinstance(m.c_field, MyModel.TextEnum) assert m.c_field == MyModel.TextEnum.C_FOO assert m.get_c_field_display() == "T Foo Description" assert m.c_field_nullable is None assert m.get_c_field_nullable_display() is None assert m.c_field_with_empty_state_nullable is None assert ( m.get_c_field_with_empty_state_nullable_display() == MyModel.TextEnumWithEmptyStateLabel.__empty__ ) def test_int_field_get_display(db): m = MyModel() assert isinstance(m.i_field, MyModel.IntegerEnum) assert m.i_field == MyModel.IntegerEnum.I_FOO assert m.get_i_field_display() == "I Foo Description" assert m.i_field_nullable is None assert m.get_i_field_nullable_display() is None assert m.i_field_with_empty_state_nullable is None assert ( m.get_i_field_with_empty_state_nullable_display() == MyModel.IntegerEnumWithEmptyStateLabel.__empty__ ) def test_int_flag_field_get_display(db): m = MyModel() assert isinstance(m.if_field, MyModel.IntegerFlagEnum) assert m.if_field == MyModel.IntegerFlagEnum.IF_FOO assert m.get_if_field_display() == "IF Foo Description" assert m.if_field_nullable is None assert m.get_if_field_nullable_display() is None assert m.if_field_with_empty_state_nullable is None assert ( m.get_if_field_with_empty_state_nullable_display() == MyModel.IntegerFlagEnumWithEmptyStateLabel.__empty__ ) def test_textchoices_field_blank_without_null_raises_error(): class DummyEnum(models.TextChoices): FOO = "foo", "Foo" BAR = "bar", "Bar" with pytest.raises(ImproperlyConfigured) as exc: TextChoicesField( choices_enum=DummyEnum, blank=True, null=False, ) assert str(exc.value) == "TextChoicesField with blank=True must also have null=True." def test_integerchoices_field_blank_without_null_raises_error(): class DummyEnum(models.IntegerChoices): FOO = 1, "Foo" BAR = 2, "Bar" with pytest.raises(ImproperlyConfigured) as exc: IntegerChoicesField( choices_enum=DummyEnum, blank=True, null=False, ) assert str(exc.value) == "IntegerChoicesField with blank=True must also have null=True." def test_integerchoicesflag_field_blank_without_null_raises_error(): class DummyEnum(IntegerChoicesFlag): FOO = 1, "Foo" BAR = 2, "Bar" with pytest.raises(ImproperlyConfigured) as exc: IntegerChoicesFlagField( choices_enum=DummyEnum, blank=True, null=False, ) assert str(exc.value) == "IntegerChoicesFlagField with blank=True must also have null=True." def test_textchoices_field_with_choices_parameter(): field = TextChoicesField( choices=[("foo", "Foo"), ("bar", "Bar")], default="foo", ) # Test that it works assert field.to_python("foo").value == "foo" assert field.to_python("bar").value == "bar" def test_textchoices_field_with_choices_blank_without_null(): with pytest.raises(ImproperlyConfigured) as exc: TextChoicesField( choices=[("foo", "Foo"), ("bar", "Bar")], blank=True, null=False, ) assert str(exc.value) == "TextChoicesField with blank=True must also have null=True." def test_integerchoices_field_with_choices_parameter(): field = IntegerChoicesField( choices=[(1, "One"), (2, "Two")], default=1, ) # Test that it works assert field.to_python(1).value == 1 assert field.to_python(2).value == 2 def test_textchoices_field_without_choices_or_enum_raises_error(): with pytest.raises(TypeError) as exc: TextChoicesField() assert str(exc.value) == "either of choices_enum or choices must be provided" def test_integerchoices_field_without_choices_or_enum_raises_error(): with pytest.raises(TypeError) as exc: IntegerChoicesField() assert str(exc.value) == "either of choices_enum or choices must be provided" def test_integerchoicesflag_field_without_choices_or_enum_raises_error(): with pytest.raises(TypeError) as exc: IntegerChoicesFlagField() assert str(exc.value) == "either of choices_enum or choices must be provided" django-choices-field-4.0.0/tests/typecheckers/000077500000000000000000000000001512374361600213145ustar00rootroot00000000000000django-choices-field-4.0.0/tests/typecheckers/__init__.py000066400000000000000000000000001512374361600234130ustar00rootroot00000000000000django-choices-field-4.0.0/tests/typecheckers/test_fields.py000066400000000000000000000145561512374361600242060ustar00rootroot00000000000000import pytest from inline_snapshot import snapshot from .utils import ( Result, requires_mypy, requires_pyright, requires_ty, typecheck, ) pytestmark = [requires_pyright, requires_mypy, requires_ty] CODE = """ from django.db import models from django_choices_field import IntegerChoicesField, TextChoicesField from django_choices_field.fields import IntegerChoicesFlagField from django_choices_field.types import IntegerChoicesFlag class TextEnum(models.TextChoices): FOO = "foo", "Foo Description" BAR = "bar", "Bar Description" class IntegerEnum(models.IntegerChoices): FOO = 1, "Foo Description" BAR = 2, "Bar Description" class IntegerFlagEnum(IntegerChoicesFlag): FOO = 1 BAR = 2 class MyModel(models.Model): # TextChoicesField - non-null text_field = TextChoicesField( choices_enum=TextEnum, default=TextEnum.FOO, ) # TextChoicesField - nullable text_field_nullable = TextChoicesField( choices_enum=TextEnum, null=True, ) # IntegerChoicesField - non-null int_field = IntegerChoicesField( choices_enum=IntegerEnum, default=IntegerEnum.FOO, ) # IntegerChoicesField - nullable int_field_nullable = IntegerChoicesField( choices_enum=IntegerEnum, null=True, ) # IntegerChoicesFlagField - non-null flag_field = IntegerChoicesFlagField( choices_enum=IntegerFlagEnum, default=IntegerFlagEnum.FOO, ) # IntegerChoicesFlagField - nullable flag_field_nullable = IntegerChoicesFlagField( choices_enum=IntegerFlagEnum, null=True, ) class Meta: app_label = "test" instance = MyModel() # Reveal types for non-null fields reveal_type(instance.text_field) reveal_type(instance.int_field) reveal_type(instance.flag_field) # Reveal types for nullable fields reveal_type(instance.text_field_nullable) reveal_type(instance.int_field_nullable) reveal_type(instance.flag_field_nullable) """ def test_field_types(subtests: pytest.Subtests): results = typecheck(CODE) with subtests.test(msg="pyright"): assert results.pyright == snapshot( [ Result( type="information", message='Type of "instance.text_field" is "TextEnum"', line=63, column=13, ), Result( type="information", message='Type of "instance.int_field" is "IntegerEnum"', line=64, column=13, ), Result( type="information", message='Type of "instance.flag_field" is "IntegerFlagEnum"', line=65, column=13, ), Result( type="information", message='Type of "instance.text_field_nullable" is "TextEnum | None"', line=68, column=13, ), Result( type="information", message='Type of "instance.int_field_nullable" is "IntegerEnum | None"', line=69, column=13, ), Result( type="information", message='Type of "instance.flag_field_nullable" is "IntegerFlagEnum | None"', line=70, column=13, ), ] ) with subtests.test(msg="mypy"): assert results.mypy == snapshot( [ Result( type="note", message='Revealed type is "mypy_test.TextEnum"', line=63, column=13, ), Result( type="note", message='Revealed type is "mypy_test.IntegerEnum"', line=64, column=13, ), Result( type="note", message='Revealed type is "mypy_test.IntegerFlagEnum"', line=65, column=13, ), Result( type="note", message='Revealed type is "mypy_test.TextEnum | None"', line=68, column=13, ), Result( type="note", message='Revealed type is "mypy_test.IntegerEnum | None"', line=69, column=13, ), Result( type="note", message='Revealed type is "mypy_test.IntegerFlagEnum | None"', line=70, column=13, ), ] ) # ty doesn't fully support Django model fields yet. # See: https://github.com/astral-sh/ty/issues/1018 with subtests.test(msg="ty"): pytest.xfail("ty doesn't support Django model fields yet") assert results.ty == snapshot( [ Result( type="information", message="Revealed type: `TextEnum`", line=63, column=13, ), Result( type="information", message="Revealed type: `IntegerEnum`", line=64, column=13, ), Result( type="information", message="Revealed type: `IntegerFlagEnum`", line=65, column=13, ), Result( type="information", message="Revealed type: `TextEnum | None`", line=68, column=13, ), Result( type="information", message="Revealed type: `IntegerEnum | None`", line=69, column=13, ), Result( type="information", message="Revealed type: `IntegerFlagEnum | None`", line=70, column=13, ), ] ) django-choices-field-4.0.0/tests/typecheckers/utils/000077500000000000000000000000001512374361600224545ustar00rootroot00000000000000django-choices-field-4.0.0/tests/typecheckers/utils/__init__.py000066400000000000000000000030321512374361600245630ustar00rootroot00000000000000from __future__ import annotations import concurrent.futures import shutil from dataclasses import dataclass import pytest from .mypy import run_mypy from .pyright import run_pyright from .result import Result from .ty import run_ty __all__ = [ "Result", "TypecheckResult", "requires_mypy", "requires_pyright", "requires_ty", "typecheck", ] def pyright_exist() -> bool: return shutil.which("pyright") is not None def mypy_exists() -> bool: return shutil.which("mypy") is not None def ty_exists() -> bool: return shutil.which("ty") is not None requires_pyright = pytest.mark.skipif( not pyright_exist(), reason="These tests require pyright", ) requires_mypy = pytest.mark.skipif( not mypy_exists(), reason="These tests require mypy", ) requires_ty = pytest.mark.skipif( not ty_exists(), reason="These tests require ty", ) @dataclass class TypecheckResult: pyright: list[Result] mypy: list[Result] ty: list[Result] def typecheck(code: str, strict: bool = True) -> TypecheckResult: with concurrent.futures.ThreadPoolExecutor() as executor: pyright_future = executor.submit(run_pyright, code, strict=strict) mypy_future = executor.submit(run_mypy, code, strict=strict) ty_future = executor.submit(run_ty, code, strict=strict) pyright_results = pyright_future.result() mypy_results = mypy_future.result() ty_results = ty_future.result() return TypecheckResult(pyright=pyright_results, mypy=mypy_results, ty=ty_results) django-choices-field-4.0.0/tests/typecheckers/utils/mypy.py000066400000000000000000000031641512374361600240300ustar00rootroot00000000000000from __future__ import annotations import json import os import pathlib import subprocess import tempfile from .result import Result def run_mypy(code: str, strict: bool = True) -> list[Result]: args = ["mypy", "--output=json"] if strict: args.append("--strict") with tempfile.TemporaryDirectory() as directory: module_path = pathlib.Path(directory) / "mypy_test.py" module_path.write_text(code) process_result = subprocess.run( [*args, str(module_path)], check=False, capture_output=True, env={ "PYTHONWARNINGS": "error,ignore::SyntaxWarning", "PATH": os.environ["PATH"], }, ) full_output = ( process_result.stdout.decode("utf-8") + "\n" + process_result.stderr.decode("utf-8") ) full_output = full_output.strip() results: list[Result] = [] try: for line in full_output.split("\n"): if not line.strip(): continue mypy_result = json.loads(line) results.append( Result( type=mypy_result["severity"].strip(), message=mypy_result["message"].strip(), line=mypy_result["line"], column=mypy_result["column"] + 1, ) ) except json.JSONDecodeError as e: raise ValueError(f"Invalid JSON: {full_output}") from e results.sort(key=lambda x: (x.line, x.column, x.message)) return results django-choices-field-4.0.0/tests/typecheckers/utils/pyright.py000066400000000000000000000030241512374361600245130ustar00rootroot00000000000000from __future__ import annotations import json import subprocess import tempfile from typing import TypedDict, cast from .result import Result, ResultType class PyrightCLIResult(TypedDict): version: str time: str generalDiagnostics: list[GeneralDiagnostic] summary: Summary class GeneralDiagnostic(TypedDict): file: str severity: str message: str range: Range class Range(TypedDict): start: EndOrStart end: EndOrStart class EndOrStart(TypedDict): line: int character: int class Summary(TypedDict): filesAnalyzed: int errorCount: int warningCount: int informationCount: int timeInSec: float def run_pyright(code: str, strict: bool = True) -> list[Result]: if strict: code = "# pyright: strict\n" + code with tempfile.NamedTemporaryFile("w", suffix=".py") as f: f.write(code) f.flush() process_result = subprocess.run( ["pyright", "--outputjson", f.name], capture_output=True, check=False ) pyright_result: PyrightCLIResult = json.loads(process_result.stdout.decode("utf-8")) result = [ Result( type=cast("ResultType", diagnostic["severity"].strip()), message=diagnostic["message"].strip(), line=diagnostic["range"]["start"]["line"], column=diagnostic["range"]["start"]["character"] + 1, ) for diagnostic in pyright_result["generalDiagnostics"] ] result.sort(key=lambda x: (x.line, x.column, x.message)) return result django-choices-field-4.0.0/tests/typecheckers/utils/result.py000066400000000000000000000003411512374361600243420ustar00rootroot00000000000000from dataclasses import dataclass from typing import Literal ResultType = Literal[ "error", "information", "note", ] @dataclass class Result: type: ResultType message: str line: int column: int django-choices-field-4.0.0/tests/typecheckers/utils/ty.py000066400000000000000000000040431512374361600234630ustar00rootroot00000000000000from __future__ import annotations import os import pathlib import re import subprocess import tempfile from typing import cast from .result import Result, ResultType def run_ty(code: str, strict: bool = True) -> list[Result]: args = [ "ty", "check", "--output-format", "concise", "--ignore", "undefined-reveal", "--color", "never", ] with tempfile.TemporaryDirectory() as directory: module_path = pathlib.Path(directory) / "ty_test.py" module_path.write_text(code) process_result = subprocess.run( [*args, str(module_path)], check=False, capture_output=True, env={ "PATH": os.environ["PATH"], }, ) full_output = ( process_result.stdout.decode("utf-8") + "\n" + process_result.stderr.decode("utf-8") ) full_output = full_output.strip() results: list[Result] = [] pattern = re.compile(r"^.*?:(\d+):(\d+): (error|warning|info)\[[\w-]+\] (.+)$") for raw_line in full_output.split("\n"): line = raw_line.strip() if not line: continue match = pattern.match(line) if match: line_num = int(match.group(1)) col_num = int(match.group(2)) severity = match.group(3) message = match.group(4) type_mapping = { "error": "error", "warning": "error", "info": "information", } result_type = type_mapping.get(severity, "note") results.append( Result( type=cast("ResultType", result_type), message=message.strip(), line=line_num, column=col_num, ) ) results.sort(key=lambda x: (x.line, x.column, x.message)) return results