pax_global_header00006660000000000000000000000064145726611560014527gustar00rootroot0000000000000052 comment=193ace75ec76467f5f0043c5d2af184d1fdca3fc django-choices-field-2.3.0/000077500000000000000000000000001457266115600154675ustar00rootroot00000000000000django-choices-field-2.3.0/.coveragerc000066400000000000000000000003441457266115600176110ustar00rootroot00000000000000[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-2.3.0/.flake8000066400000000000000000000002351457266115600166420ustar00rootroot00000000000000[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-2.3.0/.github/000077500000000000000000000000001457266115600170275ustar00rootroot00000000000000django-choices-field-2.3.0/.github/workflows/000077500000000000000000000000001457266115600210645ustar00rootroot00000000000000django-choices-field-2.3.0/.github/workflows/cicd.yml000066400000000000000000000040131457266115600225070ustar00rootroot00000000000000--- 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: matrix: django-version: - 3.2.* - 4.0.* - 4.1.* - 4.2.* - 5.0.* python-version: - '3.8' - '3.9' - '3.10' - '3.11' - '3.12' exclude: # Django 5.0 only supports python 3.10+ - django-version: 5.0.* python-version: '3.8' - django-version: 5.0.* python-version: '3.9' 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@v4.1.0 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-2.3.0/.gitignore000066400000000000000000000023641457266115600174640ustar00rootroot00000000000000# 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-2.3.0/.pre-commit-config.yaml000066400000000000000000000021351457266115600217510ustar00rootroot00000000000000--- repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer exclude: | (?x)( tests/data/schema.gql ) - id: check-added-large-files args: - --maxkb=1024 - id: check-docstring-first - id: check-merge-conflict - id: check-yaml - id: check-toml - id: check-json - id: check-xml - id: check-symlinks - repo: https://github.com/codespell-project/codespell rev: v2.2.5 hooks: - id: codespell types_or: - cython - pyi - python - markdown - repo: https://github.com/asottile/pyupgrade rev: v3.7.0 hooks: - id: pyupgrade args: - --py37-plus - repo: https://github.com/charliermarsh/ruff-pre-commit rev: v0.0.274 hooks: - id: ruff args: - --fix - repo: https://github.com/psf/black rev: 24.2.0 hooks: - id: black args: - --config - pyproject.toml django-choices-field-2.3.0/.vim/000077500000000000000000000000001457266115600163405ustar00rootroot00000000000000django-choices-field-2.3.0/.vim/coc-settings.json000066400000000000000000000003661457266115600216420ustar00rootroot00000000000000{ "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-2.3.0/LICENSE000066400000000000000000000020651457266115600164770ustar00rootroot00000000000000The 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-2.3.0/README.md000066400000000000000000000054061457266115600167530ustar00rootroot00000000000000# 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-2.3.0/django_choices_field/000077500000000000000000000000001457266115600215715ustar00rootroot00000000000000django-choices-field-2.3.0/django_choices_field/__init__.py000066400000000000000000000002741457266115600237050ustar00rootroot00000000000000from .fields import IntegerChoicesField, TextChoicesField from .types import IntegerChoicesFlag __all__ = [ "IntegerChoicesFlag", "TextChoicesField", "IntegerChoicesField", ] django-choices-field-2.3.0/django_choices_field/fields.py000066400000000000000000000134311457266115600234130ustar00rootroot00000000000000import functools import itertools from typing import Callable, ClassVar, Dict, Optional, Sequence, Type, cast from django.core.exceptions import 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) 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): 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], verbose_name: Optional[str] = None, name: Optional[str] = None, **kwargs, ): self.choices_enum = choices_enum kwargs["choices"] = choices_enum.choices kwargs.setdefault("max_length", max(len(c.value) for c in choices_enum)) super().__init__(verbose_name=verbose_name, name=name, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() kwargs["choices_enum"] = self.choices_enum return name, path, args, kwargs def to_python(self, value): if value is None: return None try: return self.choices_enum(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) class IntegerChoicesField(models.IntegerField): 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], verbose_name: Optional[str] = None, name: Optional[str] = None, **kwargs, ): self.choices_enum = choices_enum kwargs["choices"] = choices_enum.choices super().__init__(verbose_name=verbose_name, name=name, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() kwargs["choices_enum"] = self.choices_enum return name, path, args, kwargs 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): 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], verbose_name: Optional[str] = None, name: Optional[str] = None, **kwargs, ): self.choices_enum = choices_enum default_choices = choices_enum.choices kwargs["choices"] = default_choices[:] 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) kwargs["choices"].append((value, desc)) super().__init__(verbose_name=verbose_name, name=name, **kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() kwargs["choices_enum"] = self.choices_enum return name, path, args, kwargs 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-2.3.0/django_choices_field/fields.pyi000066400000000000000000000160311457266115600235630ustar00rootroot00000000000000from typing import ( Any, Callable, Dict, Generic, Iterable, Literal, Optional, TypeVar, overload, ) from django.db.models import Field, IntegerChoices, TextChoices from typing_extensions import TypeAlias from django_choices_field.types import IntegerChoicesFlag _ValidatorCallable: TypeAlias = Callable[..., None] _ErrorMessagesToOverride: TypeAlias = Dict[str, Any] _C = TypeVar("_C", bound=Optional[TextChoices]) class TextChoicesField(Generic[_C], Field[_C, _C]): choices_enum: type[_C] @overload def __new__( cls, choices_enum: type[_C], verbose_name: str | 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: str = ..., 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: str | 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: str = ..., 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=Optional[IntegerChoices]) class IntegerChoicesField(Generic[_I], Field[_I, _I]): choices_enum: type[_I] @overload def __new__( cls, choices_enum: type[_I], verbose_name: str | 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: str = ..., 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: str | 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: str = ..., 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=Optional[IntegerChoicesFlag]) class IntegerChoicesFlagField(Generic[_IF], Field[_IF, _IF]): choices_enum: type[_IF] @overload def __new__( cls, choices_enum: type[_IF], verbose_name: str | 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: str = ..., 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: str | 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: str = ..., 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-2.3.0/django_choices_field/py.typed000066400000000000000000000000001457266115600232560ustar00rootroot00000000000000django-choices-field-2.3.0/django_choices_field/types.py000066400000000000000000000011011457266115600233000ustar00rootroot00000000000000import enum import sys from typing import TYPE_CHECKING from django.db import models from typing_extensions import Self 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-2.3.0/poetry.lock000066400000000000000000001322031457266115600176640ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "asgiref" version = "3.7.2" description = "ASGI specs, helper code, and adapters" optional = false python-versions = ">=3.7" files = [ {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, ] [package.dependencies] typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] [[package]] name = "backports-zoneinfo" version = "0.2.1" description = "Backport of the standard library zoneinfo module" optional = false python-versions = ">=3.6" files = [ {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, ] [package.extras] tzdata = ["tzdata"] [[package]] name = "black" version = "24.2.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "click" version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[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.*" 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" 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.4.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "django" version = "4.2.11" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false python-versions = ">=3.8" files = [ {file = "Django-4.2.11-py3-none-any.whl", hash = "sha256:ddc24a0a8280a0430baa37aff11f28574720af05888c62b7cfe71d219f4599d3"}, {file = "Django-4.2.11.tar.gz", hash = "sha256:6e6ff3db2d8dd0c986b4eec8554c8e4f919b5c1ff62a5b4390c17aff2ed6e5c4"}, ] [package.dependencies] asgiref = ">=3.6.0,<4" "backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} 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.19.1" description = "Type stubs for Django" optional = false python-versions = ">=3.7,<4.0" files = [ {file = "django_types-0.19.1-py3-none-any.whl", hash = "sha256:b3f529de17f6374d41ca67232aa01330c531bbbaa3ac4097896f31ac33c96c30"}, {file = "django_types-0.19.1.tar.gz", hash = "sha256:5ae7988612cf6fbc357b018bbc3b3a878b65e04275cc46e0d35d66a708daff12"}, ] [package.dependencies] types-psycopg2 = ">=2.9.21.13" [[package]] name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "idna" version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "packaging" version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" 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 = "platformdirs" version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pytest" version = "8.0.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.3.0,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.7" files = [ {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "pytest-django" version = "4.8.0" description = "A Django plugin for pytest." optional = false python-versions = ">=3.8" files = [ {file = "pytest-django-4.8.0.tar.gz", hash = "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90"}, {file = "pytest_django-4.8.0-py3-none-any.whl", hash = "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7"}, ] [package.dependencies] pytest = ">=7.0.0" [package.extras] docs = ["sphinx", "sphinx-rtd-theme"] testing = ["Django", "django-configurations (>=2.0)"] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [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 = "ruff" version = "0.0.274" description = "An extremely fast Python linter, written in Rust." optional = false python-versions = ">=3.7" files = [ {file = "ruff-0.0.274-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:6d7069157a5674be8090c7d89fa69dbb2478f333a80b1312d20ade2a870cca3e"}, {file = "ruff-0.0.274-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:fa9ad8776cb92f2739b8eee316cde841d6012d0eafbf06bae09166203ddf9bb8"}, {file = "ruff-0.0.274-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6133fa856b230a0281197aeba3e89ce9595f9d8a7265113520ad259d416c9f4b"}, {file = "ruff-0.0.274-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:023cdc5e64b3f34244e12bd236ab412c6056061a2415d50fb862cab333b4ab7c"}, {file = "ruff-0.0.274-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34e71a06a4e27554ca2f1c2b1749a802e3e76cac41481cfc2cb86936c1e37d3c"}, {file = "ruff-0.0.274-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5137f853fffa7352901038a1b5e36a97058380a554763e534aae9f6c0734fdab"}, {file = "ruff-0.0.274-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044084f039a679e557240fefcf521e057941b163014cf969ac6d04620861e04a"}, {file = "ruff-0.0.274-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dca36d651f88b4b24f17df5db54487057ce0ccd3599cbf38d237cf4d9f0d63c2"}, {file = "ruff-0.0.274-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:587130943110e9536018ce139158e2962d46623f379a4486aabfd525e9714b0e"}, {file = "ruff-0.0.274-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:39b80156ef57b33ab7bfc454b7c2678eaae07f43a7dcf2e7c058f72d87b49538"}, {file = "ruff-0.0.274-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:86d943107f20fec924f56dc4933cefb0efbc1ec8b729e0a1afabac598c5586ca"}, {file = "ruff-0.0.274-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b13e765b64487143f05e6ad6f624388b9ac5a6ff8657ff801091c9282de3ca52"}, {file = "ruff-0.0.274-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:461bda8c3a4c1591fd7a09960b86e2ffc9a59a1c42cf14d725077314c12b60b0"}, {file = "ruff-0.0.274-py3-none-win32.whl", hash = "sha256:09c87fa2c4f53bd63c1d8a35150683794b022f4f2fbd6ef2fe383ce21ab53fec"}, {file = "ruff-0.0.274-py3-none-win_amd64.whl", hash = "sha256:e80aa0c7e1347a96db846287695971925eb56760c019a7d66312fbca389a6800"}, {file = "ruff-0.0.274-py3-none-win_arm64.whl", hash = "sha256:8b99ce776fc60fb938791f558b297e53ed634adeef1a7b84a33455924948c9af"}, {file = "ruff-0.0.274.tar.gz", hash = "sha256:c7e5f9deffbd02d8054f90b565a1106faee64e16cedf50f3aa05c14b59ff8727"}, ] [[package]] name = "sqlparse" version = "0.4.4" description = "A non-validating SQL parser." optional = false python-versions = ">=3.5" files = [ {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, ] [package.extras] dev = ["build", "flake8"] doc = ["sphinx"] test = ["pytest", "pytest-cov"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "types-psycopg2" version = "2.9.21.20240218" description = "Typing stubs for psycopg2" optional = false python-versions = ">=3.8" files = [ {file = "types-psycopg2-2.9.21.20240218.tar.gz", hash = "sha256:3084cd807038a62c80fb5be78b41d855b48a060316101ea59fd85c302efb57d4"}, {file = "types_psycopg2-2.9.21.20240218-py3-none-any.whl", hash = "sha256:cac96264e063cbce28dee337a973d39e6df4ca671252343cb4f8e5ef6db5e67d"}, ] [[package]] name = "typing-extensions" version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] name = "tzdata" version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] name = "urllib3" version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0" content-hash = "83c089ed09170b01c40fdbe473b6afc8873cc8391d203e5010e58e6a9a100cd1" django-choices-field-2.3.0/pyproject.toml000066400000000000000000000064741457266115600204160ustar00rootroot00000000000000[tool.poetry] name = "django-choices-field" version = "2.3.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.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Framework :: Django", "Framework :: Django :: 3.2", "Framework :: Django :: 4.0", "Framework :: Django :: 4.1", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", ] packages = [{ include = "django_choices_field" }] [tool.poetry.dependencies] python = ">=3.8,<4.0" django = ">=3.2" typing_extensions = ">=4.0.0" [tool.poetry.dev-dependencies] black = "^24.2.0" codecov = "^2.1.11" django = "^4.0" django-types = "^0.19.1" pytest = "^8.0.2" pytest-cov = "^4.0.0" pytest-django = "^4.2.0" ruff = "^0.0.274" [tool.black] line-length = 100 target-version = ['py38'] preview = true exclude = ''' /( \.eggs | \.git | \.hg | \.mypy_cache | \.tox | \.venv | __pycached__ | _build | buck-out | build | dist )/ ''' [tool.ruff] line-length = 100 select = [ "E", "F", "W", "I", "N", "D", "UP", "YTT", "D2", "D3", "D4", "BLE", "B", "A", "COM", "C4", "DTZ", "T10", "EXE", "ISC", "ICN001", "G", "INP", "PIE", "T20", "PYI", "PT", "Q", "RET", "SIM", "TID", "TCH", "PTH", "ERA", "PGH", "PL", "RSE", "RUF", "TRY", "SLF", ] ignore = [ "D1", "D203", "D213", "TCH001", "TCH002", "TCH003", "PGH003", "PLR09", "PLR2004", "SLF001", "TRY003", ] target-version = "py37" exclude = [ ".eggs", ".git", ".hg", ".mypy_cache", ".tox", ".venv", "__pycached__", "_build", "buck-out", "build", "dist", ] [tool.ruff.isort] [tool.ruff.pydocstyle] convention = "google" [tool.pyright] pythonVersion = "3.8" useLibraryCodeForTypes = true venvPath = "." venv = ".venv" ignore = ["**/migrations"] 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 = "tests/test_*.py" addopts = "--cov=./ -vv" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" django-choices-field-2.3.0/tests/000077500000000000000000000000001457266115600166315ustar00rootroot00000000000000django-choices-field-2.3.0/tests/__init__.py000066400000000000000000000000001457266115600207300ustar00rootroot00000000000000django-choices-field-2.3.0/tests/models.py000066400000000000000000000047361457266115600205000ustar00rootroot00000000000000import 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 IntegerEnum(models.IntegerChoices): 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 ) objects = models.Manager["MyModel"]() # type: ignore c_field = TextChoicesField( choices_enum=TextEnum, default=TextEnum.C_FOO, ) c_field_nullable = TextChoicesField( choices_enum=TextEnum, null=True, ) i_field = IntegerChoicesField( choices_enum=IntegerEnum, default=IntegerEnum.I_FOO, ) i_field_nullable = IntegerChoicesField( choices_enum=IntegerEnum, 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, ) django-choices-field-2.3.0/tests/settings.py000066400000000000000000000012631457266115600210450ustar00rootroot00000000000000from 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-2.3.0/tests/test_fields.py000066400000000000000000000160541457266115600215160ustar00rootroot00000000000000import sys import pytest from django.core.exceptions import ValidationError 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"), ] @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"), ] @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_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 @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}'."