Merge pull request #372 from collerek/add_construct
Release 0.10.21 - construct and validators in generated pydantic models
This commit is contained in:
28
.pre-commit-config.yaml
Normal file
28
.pre-commit-config.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 21.9b0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 3.9.2
|
||||
hooks:
|
||||
- id: flake8
|
||||
args: [ '--max-line-length=88' ]
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.910
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: [--no-strict-optional, --ignore-missing-imports]
|
||||
additional_dependencies: [
|
||||
types-ujson>=0.1.1,
|
||||
types-PyMySQL>=1.0.2,
|
||||
types-ipaddress>=1.0.0,
|
||||
types-enum34>=1.1.0,
|
||||
types-cryptography>=3.3.5,
|
||||
types-orjson>=3.6.0,
|
||||
types-aiofiles>=0.1.9,
|
||||
types-pkg-resources>=0.1.3,
|
||||
types-requests>=2.25.9,
|
||||
types-toml>=0.10.0,
|
||||
pydantic>=1.8.2
|
||||
]
|
||||
@ -17,6 +17,25 @@ especially `dict()` and `json()` methods that can also accept `exclude`, `includ
|
||||
|
||||
To read more check [pydantic][pydantic] documentation
|
||||
|
||||
## construct
|
||||
|
||||
`construct` is a raw equivalent of `__init__` method used for construction of new instances.
|
||||
|
||||
The difference is that `construct` skips validations, so it should be used when you know that data is correct and can be trusted.
|
||||
The benefit of using construct is the speed of execution due to skipped validation.
|
||||
|
||||
!!!note
|
||||
Note that in contrast to `pydantic.construct` method - the `ormar` equivalent will also process the nested related models.
|
||||
|
||||
!!!warning
|
||||
Bear in mind that due to skipped validation the `construct` method does not perform any conversions, checks etc.
|
||||
So it's your responsibility to provide tha data that is valid and can be consumed by the database.
|
||||
|
||||
The only two things that construct still performs are:
|
||||
|
||||
* Providing a `default` value for not set fields
|
||||
* Initialize nested ormar models if you pass a dictionary or a primary key value
|
||||
|
||||
## dict
|
||||
|
||||
`dict` is a method inherited from `pydantic`, yet `ormar` adds its own parameters and has some nuances when working with default values,
|
||||
@ -363,10 +382,16 @@ class Category(BaseModel):
|
||||
items: Optional[List[Item]]
|
||||
```
|
||||
|
||||
Of course you can use also deeply nested structures and ormar will generate it pydantic equivalent you (in a way that exclude loops).
|
||||
Of course, you can use also deeply nested structures and ormar will generate it pydantic equivalent you (in a way that exclude loops).
|
||||
|
||||
Note how `Item` model above does not have a reference to `Category` although in ormar the relation is bidirectional (and `ormar.Item` has `categories` field).
|
||||
|
||||
!!!warning
|
||||
Note that the generated pydantic model will inherit all **field** validators from the original `ormar` model, that includes the ormar choices validator as well as validators defined with `pydantic.validator` decorator.
|
||||
|
||||
But, at the same time all root validators present on `ormar` models will **NOT** be copied to the generated pydantic model. Since root validator can operate on all fields and a user can exclude some fields during generation of pydantic model it's not safe to copy those validators.
|
||||
If required, you need to redefine/ manually copy them to generated pydantic model.
|
||||
|
||||
## load
|
||||
|
||||
By default when you query a table without prefetching related models, the ormar will still construct
|
||||
|
||||
@ -1,3 +1,10 @@
|
||||
# 0.10.21
|
||||
|
||||
## 🐛 Fixes
|
||||
|
||||
* Add `ormar` implementation of `construct` classmethod that allows to build `Model` instances without validating the input to speed up the whole flow, if your data is already validated [#318](https://github.com/collerek/ormar/issues/318)
|
||||
* Fix for "inheriting" field validators from `ormar` model when newly created pydanic model is generated with `get_pydantic` [#365](https://github.com/collerek/ormar/issues/365)
|
||||
|
||||
# 0.10.20
|
||||
|
||||
## ✨ Features
|
||||
|
||||
@ -61,6 +61,7 @@ from ormar.fields import (
|
||||
LargeBinary,
|
||||
ManyToMany,
|
||||
ManyToManyField,
|
||||
SQL_ENCODERS_MAP,
|
||||
SmallInteger,
|
||||
String,
|
||||
Text,
|
||||
@ -132,6 +133,7 @@ __all__ = [
|
||||
"or_",
|
||||
"EncryptBackends",
|
||||
"ENCODERS_MAP",
|
||||
"SQL_ENCODERS_MAP",
|
||||
"DECODERS_MAP",
|
||||
"LargeBinary",
|
||||
"Extra",
|
||||
|
||||
@ -24,7 +24,7 @@ from ormar.fields.model_fields import (
|
||||
Time,
|
||||
UUID,
|
||||
)
|
||||
from ormar.fields.parsers import DECODERS_MAP, ENCODERS_MAP
|
||||
from ormar.fields.parsers import DECODERS_MAP, ENCODERS_MAP, SQL_ENCODERS_MAP
|
||||
from ormar.fields.sqlalchemy_encrypted import EncryptBackend, EncryptBackends
|
||||
from ormar.fields.through_field import Through, ThroughField
|
||||
|
||||
@ -54,6 +54,7 @@ __all__ = [
|
||||
"EncryptBackend",
|
||||
"DECODERS_MAP",
|
||||
"ENCODERS_MAP",
|
||||
"SQL_ENCODERS_MAP",
|
||||
"LargeBinary",
|
||||
"UniqueColumns",
|
||||
]
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import datetime
|
||||
import decimal
|
||||
import uuid
|
||||
from typing import Any, Optional, TYPE_CHECKING, Union, overload
|
||||
from enum import Enum
|
||||
from typing import Any, Optional, Set, TYPE_CHECKING, Type, Union, overload
|
||||
|
||||
import pydantic
|
||||
import sqlalchemy
|
||||
|
||||
import ormar # noqa I101
|
||||
from ormar import ModelDefinitionError # noqa I101
|
||||
from ormar.fields import sqlalchemy_uuid
|
||||
from ormar.fields.base import BaseField # noqa I101
|
||||
@ -60,6 +62,49 @@ def is_auto_primary_key(primary_key: bool, autoincrement: bool) -> bool:
|
||||
return primary_key and autoincrement
|
||||
|
||||
|
||||
def convert_choices_if_needed(
|
||||
field_type: "Type",
|
||||
choices: Set,
|
||||
nullable: bool,
|
||||
scale: int = None,
|
||||
represent_as_str: bool = False,
|
||||
) -> Set:
|
||||
"""
|
||||
Converts dates to isoformat as fastapi can check this condition in routes
|
||||
and the fields are not yet parsed.
|
||||
Converts enums to list of it's values.
|
||||
Converts uuids to strings.
|
||||
Converts decimal to float with given scale.
|
||||
|
||||
:param field_type: type o the field
|
||||
:type field_type: Type
|
||||
:param choices: set of choices
|
||||
:type choices: Set
|
||||
:param scale: scale for decimals
|
||||
:type scale: int
|
||||
:param nullable: flag if field_nullable
|
||||
:type nullable: bool
|
||||
:param represent_as_str: flag for bytes fields
|
||||
:type represent_as_str: bool
|
||||
:param scale: scale for decimals
|
||||
:type scale: int
|
||||
:return: value, choices list
|
||||
:rtype: Tuple[Any, Set]
|
||||
"""
|
||||
choices = {o.value if isinstance(o, Enum) else o for o in choices}
|
||||
encoder = ormar.ENCODERS_MAP.get(field_type, lambda x: x)
|
||||
if field_type == decimal.Decimal:
|
||||
precision = scale
|
||||
choices = {encoder(o, precision) for o in choices}
|
||||
elif field_type == bytes:
|
||||
choices = {encoder(o, represent_as_str) for o in choices}
|
||||
elif encoder:
|
||||
choices = {encoder(o) for o in choices}
|
||||
if nullable:
|
||||
choices.add(None)
|
||||
return choices
|
||||
|
||||
|
||||
class ModelFieldFactory:
|
||||
"""
|
||||
Default field factory that construct Field classes and populated their values.
|
||||
@ -96,6 +141,16 @@ class ModelFieldFactory:
|
||||
else (nullable if sql_nullable is None else sql_nullable)
|
||||
)
|
||||
|
||||
choices = set(kwargs.pop("choices", []))
|
||||
if choices:
|
||||
choices = convert_choices_if_needed(
|
||||
field_type=cls._type,
|
||||
choices=choices,
|
||||
nullable=nullable,
|
||||
scale=kwargs.get("scale", None),
|
||||
represent_as_str=kwargs.get("represent_as_base64_str", False),
|
||||
)
|
||||
|
||||
namespace = dict(
|
||||
__type__=cls._type,
|
||||
__pydantic_type__=overwrite_pydantic_type
|
||||
@ -114,7 +169,7 @@ class ModelFieldFactory:
|
||||
pydantic_only=pydantic_only,
|
||||
autoincrement=autoincrement,
|
||||
column_type=cls.get_column_type(**kwargs),
|
||||
choices=set(kwargs.pop("choices", [])),
|
||||
choices=choices,
|
||||
encrypt_secret=encrypt_secret,
|
||||
encrypt_backend=encrypt_backend,
|
||||
encrypt_custom_backend=encrypt_custom_backend,
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import base64
|
||||
import datetime
|
||||
import decimal
|
||||
from typing import Any
|
||||
import uuid
|
||||
from typing import Any, Callable, Dict, Union
|
||||
|
||||
import pydantic
|
||||
from pydantic.datetime_parse import parse_date, parse_datetime, parse_time
|
||||
@ -19,21 +21,55 @@ def encode_bool(value: bool) -> str:
|
||||
return "true" if value else "false"
|
||||
|
||||
|
||||
def encode_decimal(value: decimal.Decimal, precision: int = None) -> float:
|
||||
if precision:
|
||||
return (
|
||||
round(float(value), precision)
|
||||
if isinstance(value, decimal.Decimal)
|
||||
else value
|
||||
)
|
||||
return float(value)
|
||||
|
||||
|
||||
def encode_bytes(value: Union[str, bytes], represent_as_string: bool = False) -> bytes:
|
||||
if represent_as_string:
|
||||
return value if isinstance(value, bytes) else base64.b64decode(value)
|
||||
return value if isinstance(value, bytes) else value.encode("utf-8")
|
||||
|
||||
|
||||
def encode_json(value: Any) -> str:
|
||||
value = json.dumps(value) if not isinstance(value, str) else value
|
||||
value = json.dumps(value) if not isinstance(value, str) else re_dump_value(value)
|
||||
value = value.decode("utf-8") if isinstance(value, bytes) else value
|
||||
return value
|
||||
|
||||
|
||||
ENCODERS_MAP = {
|
||||
bool: encode_bool,
|
||||
def re_dump_value(value: str) -> Union[str, bytes]:
|
||||
"""
|
||||
Rw-dumps choices due to different string representation in orjson and json
|
||||
:param value: string to re-dump
|
||||
:type value: str
|
||||
:return: re-dumped choices
|
||||
:rtype: List[str]
|
||||
"""
|
||||
try:
|
||||
result: Union[str, bytes] = json.dumps(json.loads(value))
|
||||
except json.JSONDecodeError:
|
||||
result = value
|
||||
return result
|
||||
|
||||
|
||||
ENCODERS_MAP: Dict[type, Callable] = {
|
||||
datetime.datetime: lambda x: x.isoformat(),
|
||||
datetime.date: lambda x: x.isoformat(),
|
||||
datetime.time: lambda x: x.isoformat(),
|
||||
pydantic.Json: encode_json,
|
||||
decimal.Decimal: float,
|
||||
decimal.Decimal: encode_decimal,
|
||||
uuid.UUID: str,
|
||||
bytes: encode_bytes,
|
||||
}
|
||||
|
||||
SQL_ENCODERS_MAP: Dict[type, Callable] = {bool: encode_bool, **ENCODERS_MAP}
|
||||
|
||||
DECODERS_MAP = {
|
||||
bool: parse_bool,
|
||||
datetime.datetime: parse_datetime,
|
||||
|
||||
@ -160,7 +160,7 @@ class EncryptedString(types.TypeDecorator):
|
||||
try:
|
||||
value = self._underlying_type.process_bind_param(value, dialect)
|
||||
except AttributeError:
|
||||
encoder = ormar.ENCODERS_MAP.get(self.type_, None)
|
||||
encoder = ormar.SQL_ENCODERS_MAP.get(self.type_, None)
|
||||
if encoder:
|
||||
value = encoder(value) # type: ignore
|
||||
|
||||
|
||||
@ -50,9 +50,10 @@ def get_pydantic_field(field_name: str, model: Type["Model"]) -> "ModelField":
|
||||
:return: newly created pydantic field
|
||||
:rtype: pydantic.ModelField
|
||||
"""
|
||||
type_ = model.Meta.model_fields[field_name].__type__
|
||||
return ModelField(
|
||||
name=field_name,
|
||||
type_=model.Meta.model_fields[field_name].__type__, # type: ignore
|
||||
type_=type_, # type: ignore
|
||||
model_config=model.__config__,
|
||||
required=not model.Meta.model_fields[field_name].nullable,
|
||||
class_validators={},
|
||||
|
||||
@ -101,6 +101,7 @@ def register_reverse_model_fields(model_field: "ForeignKeyField") -> None:
|
||||
:type model_field: relation Field
|
||||
"""
|
||||
related_name = model_field.get_related_name()
|
||||
# TODO: Reverse relations does not register pydantic fields?
|
||||
if model_field.is_multi:
|
||||
model_field.to.Meta.model_fields[related_name] = ManyToMany( # type: ignore
|
||||
model_field.owner,
|
||||
|
||||
@ -1,30 +1,37 @@
|
||||
import base64
|
||||
import datetime
|
||||
import decimal
|
||||
import numbers
|
||||
import uuid
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Set, TYPE_CHECKING, Tuple, Type, Union
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
List,
|
||||
Set,
|
||||
TYPE_CHECKING,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
try:
|
||||
import orjson as json
|
||||
except ImportError: # pragma: no cover
|
||||
import json # type: ignore
|
||||
import json # type: ignore # noqa: F401
|
||||
|
||||
import pydantic
|
||||
from pydantic.fields import SHAPE_LIST
|
||||
from pydantic.class_validators import make_generic_validator
|
||||
from pydantic.fields import ModelField, SHAPE_LIST
|
||||
from pydantic.main import SchemaExtraCallable
|
||||
|
||||
import ormar # noqa: I100, I202
|
||||
from ormar.fields import BaseField
|
||||
from ormar.models.helpers.models import meta_field_not_set
|
||||
from ormar.queryset.utils import translate_list_to_dict
|
||||
|
||||
if TYPE_CHECKING: # pragma no cover
|
||||
from ormar import Model
|
||||
from ormar.fields import BaseField
|
||||
|
||||
|
||||
def check_if_field_has_choices(field: BaseField) -> bool:
|
||||
def check_if_field_has_choices(field: "BaseField") -> bool:
|
||||
"""
|
||||
Checks if given field has choices populated.
|
||||
A if it has one, a validator for this field needs to be attached.
|
||||
@ -37,110 +44,56 @@ def check_if_field_has_choices(field: BaseField) -> bool:
|
||||
return hasattr(field, "choices") and bool(field.choices)
|
||||
|
||||
|
||||
def convert_choices_if_needed( # noqa: CCR001
|
||||
field: "BaseField", value: Any
|
||||
) -> Tuple[Any, List]:
|
||||
def convert_value_if_needed(field: "BaseField", value: Any) -> Any:
|
||||
"""
|
||||
Converts dates to isoformat as fastapi can check this condition in routes
|
||||
and the fields are not yet parsed.
|
||||
|
||||
Converts enums to list of it's values.
|
||||
|
||||
Converts uuids to strings.
|
||||
|
||||
Converts decimal to float with given scale.
|
||||
|
||||
:param field: ormar field to check with choices
|
||||
:type field: BaseField
|
||||
:param value: current values of the model to verify
|
||||
:type value: Dict
|
||||
:return: value, choices list
|
||||
:rtype: Tuple[Any, List]
|
||||
"""
|
||||
# TODO use same maps as with EncryptedString
|
||||
choices = [o.value if isinstance(o, Enum) else o for o in field.choices]
|
||||
|
||||
if field.__type__ in [datetime.datetime, datetime.date, datetime.time]:
|
||||
value = value.isoformat() if not isinstance(value, str) else value
|
||||
choices = [o.isoformat() for o in field.choices]
|
||||
elif field.__type__ == pydantic.Json:
|
||||
value = (
|
||||
json.dumps(value) if not isinstance(value, str) else re_dump_value(value)
|
||||
)
|
||||
value = value.decode("utf-8") if isinstance(value, bytes) else value
|
||||
choices = [re_dump_value(x) for x in field.choices]
|
||||
elif field.__type__ == uuid.UUID:
|
||||
value = str(value) if not isinstance(value, str) else value
|
||||
choices = [str(o) for o in field.choices]
|
||||
elif field.__type__ == decimal.Decimal:
|
||||
precision = field.scale # type: ignore
|
||||
value = (
|
||||
round(float(value), precision)
|
||||
if isinstance(value, decimal.Decimal)
|
||||
else value
|
||||
)
|
||||
choices = [round(float(o), precision) for o in choices]
|
||||
elif field.__type__ == bytes:
|
||||
if field.represent_as_base64_str:
|
||||
value = value if isinstance(value, bytes) else base64.b64decode(value)
|
||||
else:
|
||||
value = value if isinstance(value, bytes) else value.encode("utf-8")
|
||||
|
||||
return value, choices
|
||||
|
||||
|
||||
def re_dump_value(value: str) -> str:
|
||||
"""
|
||||
Rw-dumps choices due to different string representation in orjson and json
|
||||
:param value: string to re-dump
|
||||
:type value: str
|
||||
:return: re-dumped choices
|
||||
:rtype: List[str]
|
||||
"""
|
||||
try:
|
||||
result: Union[str, bytes] = json.dumps(json.loads(value))
|
||||
except json.JSONDecodeError:
|
||||
result = value
|
||||
return result.decode("utf-8") if isinstance(result, bytes) else result
|
||||
|
||||
|
||||
def validate_choices(field: "BaseField", value: Any) -> None:
|
||||
"""
|
||||
Validates if given value is in provided choices.
|
||||
|
||||
:raises ValueError: If value is not in choices.
|
||||
:param field:field to validate
|
||||
:type field: BaseField
|
||||
:param value: value of the field
|
||||
:type value: Any
|
||||
:return: value, choices list
|
||||
:rtype: Any
|
||||
"""
|
||||
value, choices = convert_choices_if_needed(field=field, value=value)
|
||||
if field.nullable:
|
||||
choices.append(None)
|
||||
if value is not ormar.Undefined and value not in choices:
|
||||
raise ValueError(
|
||||
f"{field.name}: '{value}' " f"not in allowed choices set:" f" {choices}"
|
||||
)
|
||||
encoder = ormar.ENCODERS_MAP.get(field.__type__, lambda x: x)
|
||||
if field.__type__ == decimal.Decimal:
|
||||
precision = field.scale # type: ignore
|
||||
value = encoder(value, precision)
|
||||
elif field.__type__ == bytes:
|
||||
represent_as_string = field.represent_as_base64_str
|
||||
value = encoder(value, represent_as_string)
|
||||
elif encoder:
|
||||
value = encoder(value)
|
||||
return value
|
||||
|
||||
|
||||
def choices_validator(cls: Type["Model"], values: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Validator that is attached to pydantic model pre root validators.
|
||||
Validator checks if field value is in field.choices list.
|
||||
def generate_validator(ormar_field: "BaseField") -> Callable:
|
||||
choices = ormar_field.choices
|
||||
|
||||
:raises ValueError: if field value is outside of allowed choices.
|
||||
:param cls: constructed class
|
||||
:type cls: Model class
|
||||
:param values: dictionary of field values (pydantic side)
|
||||
:type values: Dict[str, Any]
|
||||
:return: values if pass validation, otherwise exception is raised
|
||||
:rtype: Dict[str, Any]
|
||||
"""
|
||||
for field_name, field in cls.Meta.model_fields.items():
|
||||
if check_if_field_has_choices(field):
|
||||
value = values.get(field_name, ormar.Undefined)
|
||||
validate_choices(field=field, value=value)
|
||||
return values
|
||||
def validate_choices(cls: type, value: Any, field: "ModelField") -> None:
|
||||
"""
|
||||
Validates if given value is in provided choices.
|
||||
|
||||
:raises ValueError: If value is not in choices.
|
||||
:param field:field to validate
|
||||
:type field: BaseField
|
||||
:param value: value of the field
|
||||
:type value: Any
|
||||
"""
|
||||
adjusted_value = convert_value_if_needed(field=ormar_field, value=value)
|
||||
if adjusted_value is not ormar.Undefined and adjusted_value not in choices:
|
||||
raise ValueError(
|
||||
f"{field.name}: '{adjusted_value}' "
|
||||
f"not in allowed choices set:"
|
||||
f" {choices}"
|
||||
)
|
||||
return value
|
||||
|
||||
return validate_choices
|
||||
|
||||
|
||||
def generate_model_example(model: Type["Model"], relation_map: Dict = None) -> Dict:
|
||||
@ -172,7 +125,7 @@ def generate_model_example(model: Type["Model"], relation_map: Dict = None) -> D
|
||||
|
||||
|
||||
def populates_sample_fields_values(
|
||||
example: Dict[str, Any], name: str, field: BaseField, relation_map: Dict = None
|
||||
example: Dict[str, Any], name: str, field: "BaseField", relation_map: Dict = None
|
||||
) -> None:
|
||||
"""
|
||||
Iterates the field and sets fields to sample values
|
||||
@ -350,15 +303,14 @@ def populate_choices_validators(model: Type["Model"]) -> None: # noqa CCR001
|
||||
"""
|
||||
fields_with_choices = []
|
||||
if not meta_field_not_set(model=model, field_name="model_fields"):
|
||||
if hasattr(model, "_choices_fields"):
|
||||
return
|
||||
model._choices_fields = set()
|
||||
for name, field in model.Meta.model_fields.items():
|
||||
if check_if_field_has_choices(field):
|
||||
fields_with_choices.append(name)
|
||||
validators = getattr(model, "__pre_root_validators__", [])
|
||||
if choices_validator not in validators:
|
||||
validators.append(choices_validator)
|
||||
model.__pre_root_validators__ = validators
|
||||
if not model._choices_fields:
|
||||
model._choices_fields = set()
|
||||
validator = make_generic_validator(generate_validator(field))
|
||||
model.__fields__[name].validators.append(validator)
|
||||
model._choices_fields.add(name)
|
||||
|
||||
if fields_with_choices:
|
||||
|
||||
@ -106,7 +106,6 @@ def add_cached_properties(new_model: Type["Model"]) -> None:
|
||||
new_model._through_names = None
|
||||
new_model._related_fields = None
|
||||
new_model._pydantic_fields = {name for name in new_model.__fields__}
|
||||
new_model._choices_fields = set()
|
||||
new_model._json_fields = set()
|
||||
new_model._bytes_fields = set()
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import copy
|
||||
import string
|
||||
from random import choices
|
||||
from typing import (
|
||||
@ -82,7 +83,9 @@ class PydanticMixin(RelationMixin):
|
||||
(pydantic.BaseModel,),
|
||||
{"__annotations__": fields_dict, **defaults},
|
||||
)
|
||||
return cast(Type[pydantic.BaseModel], model)
|
||||
model = cast(Type[pydantic.BaseModel], model)
|
||||
cls._copy_field_validators(model=model)
|
||||
return model
|
||||
|
||||
@classmethod
|
||||
def _determine_pydantic_field_type(
|
||||
@ -111,3 +114,33 @@ class PydanticMixin(RelationMixin):
|
||||
if target is not None and field.nullable:
|
||||
target = Optional[target]
|
||||
return target
|
||||
|
||||
@classmethod
|
||||
def _copy_field_validators(cls, model: Type[pydantic.BaseModel]) -> None:
|
||||
"""
|
||||
Copy field validators from ormar model to generated pydantic model.
|
||||
"""
|
||||
for field_name, field in model.__fields__.items():
|
||||
if (
|
||||
field_name not in cls.__fields__
|
||||
or cls.Meta.model_fields[field_name].is_relation
|
||||
):
|
||||
continue
|
||||
validators = cls.__fields__[field_name].validators
|
||||
already_attached = [
|
||||
validator.__wrapped__ for validator in field.validators # type: ignore
|
||||
]
|
||||
validators_to_copy = [
|
||||
validator
|
||||
for validator in validators
|
||||
if validator.__wrapped__ not in already_attached # type: ignore
|
||||
]
|
||||
field.validators.extend(copy.deepcopy(validators_to_copy))
|
||||
class_validators = cls.__fields__[field_name].class_validators
|
||||
field.class_validators.update(copy.deepcopy(class_validators))
|
||||
field.pre_validators = copy.deepcopy(
|
||||
cls.__fields__[field_name].pre_validators
|
||||
)
|
||||
field.post_validators = copy.deepcopy(
|
||||
cls.__fields__[field_name].post_validators
|
||||
)
|
||||
|
||||
@ -11,9 +11,10 @@ from typing import (
|
||||
cast,
|
||||
)
|
||||
|
||||
import ormar
|
||||
import pydantic
|
||||
|
||||
import ormar # noqa: I100, I202
|
||||
from ormar.exceptions import ModelPersistenceError
|
||||
from ormar.models.helpers.validation import validate_choices
|
||||
from ormar.models.mixins import AliasMixin
|
||||
from ormar.models.mixins.relation_mixin import RelationMixin
|
||||
|
||||
@ -29,6 +30,7 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
||||
if TYPE_CHECKING: # pragma: nocover
|
||||
_choices_fields: Optional[Set]
|
||||
_skip_ellipsis: Callable
|
||||
__fields__: Dict[str, pydantic.fields.ModelField]
|
||||
|
||||
@classmethod
|
||||
def prepare_model_to_save(cls, new_kwargs: dict) -> dict:
|
||||
@ -180,9 +182,18 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
||||
if not cls._choices_fields:
|
||||
return new_kwargs
|
||||
|
||||
for field_name, field in cls.Meta.model_fields.items():
|
||||
if field_name in new_kwargs and field_name in cls._choices_fields:
|
||||
validate_choices(field=field, value=new_kwargs.get(field_name))
|
||||
fields_to_check = [
|
||||
field
|
||||
for field in cls.Meta.model_fields.values()
|
||||
if field.name in cls._choices_fields and field.name in new_kwargs
|
||||
]
|
||||
for field in fields_to_check:
|
||||
if new_kwargs[field.name] not in field.choices:
|
||||
raise ValueError(
|
||||
f"{field.name}: '{new_kwargs[field.name]}' "
|
||||
f"not in allowed choices set:"
|
||||
f" {field.choices}"
|
||||
)
|
||||
return new_kwargs
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -14,6 +14,7 @@ from typing import (
|
||||
TYPE_CHECKING,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
@ -50,8 +51,11 @@ if TYPE_CHECKING: # pragma no cover
|
||||
from ormar.models import Model
|
||||
from ormar.signals import SignalEmitter
|
||||
|
||||
T = TypeVar("T", bound="NewBaseModel")
|
||||
|
||||
IntStr = Union[int, str]
|
||||
DictStrAny = Dict[str, Any]
|
||||
SetStr = Set[str]
|
||||
AbstractSetIntStr = AbstractSet[IntStr]
|
||||
MappingIntStrAny = Mapping[IntStr, Any]
|
||||
|
||||
@ -86,7 +90,7 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
||||
_related_names: Optional[Set]
|
||||
_through_names: Optional[Set]
|
||||
_related_names_hash: str
|
||||
_choices_fields: Optional[Set]
|
||||
_choices_fields: Set
|
||||
_pydantic_fields: Set
|
||||
_quick_access_fields: Set
|
||||
_json_fields: Set
|
||||
@ -785,6 +789,51 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
||||
data = data["__root__"]
|
||||
return self.__config__.json_dumps(data, default=encoder, **dumps_kwargs)
|
||||
|
||||
@classmethod
|
||||
def construct(
|
||||
cls: Type["T"], _fields_set: Optional["SetStr"] = None, **values: Any
|
||||
) -> "T":
|
||||
own_values = {
|
||||
k: v for k, v in values.items() if k not in cls.extract_related_names()
|
||||
}
|
||||
model = cls.__new__(cls)
|
||||
fields_values: Dict[str, Any] = {}
|
||||
for name, field in cls.__fields__.items():
|
||||
if name in own_values:
|
||||
fields_values[name] = own_values[name]
|
||||
elif not field.required:
|
||||
fields_values[name] = field.get_default()
|
||||
fields_values.update(own_values)
|
||||
object.__setattr__(model, "__dict__", fields_values)
|
||||
model._initialize_internal_attributes()
|
||||
cls._construct_relations(model=model, values=values)
|
||||
if _fields_set is None:
|
||||
_fields_set = set(values.keys())
|
||||
object.__setattr__(model, "__fields_set__", _fields_set)
|
||||
return model
|
||||
|
||||
@classmethod
|
||||
def _construct_relations(cls: Type["T"], model: "T", values: Dict) -> None:
|
||||
present_relations = [
|
||||
relation for relation in cls.extract_related_names() if relation in values
|
||||
]
|
||||
for relation in present_relations:
|
||||
value_to_set = values[relation]
|
||||
if not isinstance(value_to_set, list):
|
||||
value_to_set = [value_to_set]
|
||||
relation_field = cls.Meta.model_fields[relation]
|
||||
relation_value = [
|
||||
relation_field.expand_relationship(x, model, to_register=False)
|
||||
for x in value_to_set
|
||||
]
|
||||
|
||||
for child in relation_value:
|
||||
model._orm.add(
|
||||
parent=cast("Model", child),
|
||||
child=cast("Model", model),
|
||||
field=cast("ForeignKeyField", relation_field),
|
||||
)
|
||||
|
||||
def update_from_dict(self, value_dict: Dict) -> "NewBaseModel":
|
||||
"""
|
||||
Updates self with values of fields passed in the dictionary.
|
||||
@ -879,6 +928,7 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
||||
:return: dictionary of fields names and values.
|
||||
:rtype: Dict
|
||||
"""
|
||||
# TODO: Cache this dictionary?
|
||||
self_fields = self._extract_own_model_fields()
|
||||
self_fields = {
|
||||
k: v
|
||||
|
||||
@ -29,8 +29,8 @@ def check_node_not_dict_or_not_last_node(
|
||||
|
||||
:param part:
|
||||
:type part: str
|
||||
:param parts:
|
||||
:type parts: List[str]
|
||||
:param is_last: flag to check if last element
|
||||
:type is_last: bool
|
||||
:param current_level: current level of the traversed structure
|
||||
:type current_level: Any
|
||||
:return: result of the check
|
||||
@ -52,7 +52,7 @@ def translate_list_to_dict( # noqa: CCR001
|
||||
Default required key ise Ellipsis like in pydantic.
|
||||
|
||||
:param list_to_trans: input list
|
||||
:type list_to_trans: set
|
||||
:type list_to_trans: Union[List, Set]
|
||||
:param is_order: flag if change affects order_by clauses are they require special
|
||||
default value with sort order.
|
||||
:type is_order: bool
|
||||
|
||||
342
poetry.lock
generated
342
poetry.lock
generated
@ -51,7 +51,7 @@ typing_extensions = ">=3.7.2"
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "3.3.2"
|
||||
version = "3.3.3"
|
||||
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -125,6 +125,21 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
|
||||
|
||||
[[package]]
|
||||
name = "backports.entry-points-selectable"
|
||||
version = "1.1.0"
|
||||
description = "Compatibility shim providing selectable entry points for older implementations"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7"
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=4.6)", "pytest-flake8", "pytest-cov", "pytest-black (>=0.3.7)", "pytest-mypy", "pytest-checkdocs (>=2.4)", "pytest-enabler (>=1.0.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "bandit"
|
||||
version = "1.7.0"
|
||||
@ -188,9 +203,17 @@ python-versions = "*"
|
||||
[package.dependencies]
|
||||
pycparser = "*"
|
||||
|
||||
[[package]]
|
||||
name = "cfgv"
|
||||
version = "3.3.1"
|
||||
description = "Validate configuration and produce human readable error messages."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "2.0.6"
|
||||
version = "2.0.7"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -201,7 +224,7 @@ unicode_backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.0.2"
|
||||
version = "8.0.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -260,7 +283,7 @@ immutables = ">=0.9"
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "6.0.1"
|
||||
version = "6.0.2"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -364,6 +387,14 @@ wrapt = ">=1.10,<2"
|
||||
[package.extras]
|
||||
dev = ["tox", "bump2version (<1)", "sphinx (<2)", "importlib-metadata (<3)", "importlib-resources (<4)", "configparser (<5)", "sphinxcontrib-websupport (<2)", "zipp (<2)", "PyTest (<5)", "PyTest-Cov (<2.6)", "pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.3"
|
||||
description = "Distribution utilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "docspec"
|
||||
version = "1.2.0"
|
||||
@ -412,6 +443,18 @@ dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<
|
||||
doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"]
|
||||
test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.3.0"
|
||||
description = "A platform independent file lock."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
|
||||
testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "3.9.2"
|
||||
@ -595,9 +638,20 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
|
||||
[package.extras]
|
||||
docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.3.0"
|
||||
description = "File identification library for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.extras]
|
||||
license = ["editdistance-s"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.2"
|
||||
version = "3.3"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -634,6 +688,21 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
perf = ["ipython"]
|
||||
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-resources"
|
||||
version = "5.2.2"
|
||||
description = "Read resources from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "1.1.1"
|
||||
@ -786,6 +855,14 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.6.0"
|
||||
description = "Node.js virtual environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "nr.fs"
|
||||
version = "1.6.3"
|
||||
@ -920,6 +997,24 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "2.15.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.dependencies]
|
||||
cfgv = ">=2.0.0"
|
||||
identify = ">=1.0.0"
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
||||
importlib-resources = {version = "*", markers = "python_version < \"3.7\""}
|
||||
nodeenv = ">=0.11.1"
|
||||
pyyaml = ">=5.1"
|
||||
toml = "*"
|
||||
virtualenv = ">=20.0.8"
|
||||
|
||||
[[package]]
|
||||
name = "psycopg2-binary"
|
||||
version = "2.9.1"
|
||||
@ -1380,6 +1475,27 @@ brotli = ["brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.8.1"
|
||||
description = "Virtual Python Environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||
|
||||
[package.dependencies]
|
||||
"backports.entry-points-selectable" = ">=1.0.4"
|
||||
distlib = ">=0.3.1,<1"
|
||||
filelock = ">=3.0.0,<4"
|
||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""}
|
||||
platformdirs = ">=2,<3"
|
||||
six = ">=1.9.0,<2"
|
||||
|
||||
[package.extras]
|
||||
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
|
||||
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "watchdog"
|
||||
version = "2.1.6"
|
||||
@ -1393,7 +1509,7 @@ watchmedo = ["PyYAML (>=3.10)"]
|
||||
|
||||
[[package]]
|
||||
name = "wrapt"
|
||||
version = "1.13.1"
|
||||
version = "1.13.2"
|
||||
description = "Module for decorators, wrappers and monkey patching."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -1434,7 +1550,7 @@ sqlite = []
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.6.2"
|
||||
content-hash = "b91a03b4747e33b324f8856d2b5efddf0ceaad773473409b0acff2668013bc4b"
|
||||
content-hash = "2941acd715d35ca51eeaae5dffe2d7a9b2bff0ae7b74d56479c2cfedccdd3e63"
|
||||
|
||||
[metadata.files]
|
||||
aiocontextvars = [
|
||||
@ -1454,8 +1570,8 @@ aiosqlite = [
|
||||
{file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"},
|
||||
]
|
||||
anyio = [
|
||||
{file = "anyio-3.3.2-py3-none-any.whl", hash = "sha256:c32da314c510b34a862f5afeaf8a446ffed2c2fde21583e654bd71ecfb5b744b"},
|
||||
{file = "anyio-3.3.2.tar.gz", hash = "sha256:0b993a2ef6c1dc456815c2b5ca2819f382f20af98087cc2090a4afed3a501436"},
|
||||
{file = "anyio-3.3.3-py3-none-any.whl", hash = "sha256:56ceaeed2877723578b1341f4f68c29081db189cfb40a97d1922b9513f6d7db6"},
|
||||
{file = "anyio-3.3.3.tar.gz", hash = "sha256:8eccec339cb4a856c94a75d50fc1d451faf32a05ef406be462e2efc59c9838b0"},
|
||||
]
|
||||
astpretty = [
|
||||
{file = "astpretty-2.1.0-py2.py3-none-any.whl", hash = "sha256:f81f14b5636f7af81fadb1e3c09ca7702ce4615500d9cc6d6829befb2dec2e3c"},
|
||||
@ -1488,6 +1604,10 @@ attrs = [
|
||||
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
|
||||
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
|
||||
]
|
||||
"backports.entry-points-selectable" = [
|
||||
{file = "backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl", hash = "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc"},
|
||||
{file = "backports.entry_points_selectable-1.1.0.tar.gz", hash = "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a"},
|
||||
]
|
||||
bandit = [
|
||||
{file = "bandit-1.7.0-py3-none-any.whl", hash = "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07"},
|
||||
{file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"},
|
||||
@ -1547,13 +1667,17 @@ cffi = [
|
||||
{file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"},
|
||||
{file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
|
||||
]
|
||||
cfgv = [
|
||||
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
|
||||
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
|
||||
]
|
||||
charset-normalizer = [
|
||||
{file = "charset-normalizer-2.0.6.tar.gz", hash = "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"},
|
||||
{file = "charset_normalizer-2.0.6-py3-none-any.whl", hash = "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6"},
|
||||
{file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"},
|
||||
{file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-8.0.2-py3-none-any.whl", hash = "sha256:3fab8aeb8f15f5452ae7511ad448977b3417325bceddd53df87e0bb81f3a8cf8"},
|
||||
{file = "click-8.0.2.tar.gz", hash = "sha256:7027bc7bbafaab8b2c2816861d8eb372429ee3c02e193fc2f93d6c4ab9de49c5"},
|
||||
{file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
|
||||
{file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
|
||||
]
|
||||
codecov = [
|
||||
{file = "codecov-2.1.12-py2.py3-none-any.whl", hash = "sha256:585dc217dc3d8185198ceb402f85d5cb5dbfa0c5f350a5abcdf9e347776a5b47"},
|
||||
@ -1575,39 +1699,39 @@ contextvars = [
|
||||
{file = "contextvars-2.4.tar.gz", hash = "sha256:f38c908aaa59c14335eeea12abea5f443646216c4e29380d7bf34d2018e2c39e"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:abe8207dfb8a61ded9cd830d26c1073c8218fc0ae17eb899cfe8ec0fafae6e22"},
|
||||
{file = "coverage-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83faa3692e8306b20293889714fdf573d10ef5efc5843bd7c7aea6971487bd6a"},
|
||||
{file = "coverage-6.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f82a17f2a77958f3eef40ad385fc82d4c6ba9a77a51a174efe03ce75daebbc16"},
|
||||
{file = "coverage-6.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b06f4f1729e2963281d9cd6e65e6976bf27b44d4c07ac5b47223ce45f822cec"},
|
||||
{file = "coverage-6.0.1-cp310-cp310-win32.whl", hash = "sha256:7600fac458f74c68b097379f76f3a6e3a630493fc7fc94b6508fedd9d498c194"},
|
||||
{file = "coverage-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:2c5f39d1556e75fc3c4fb071f9e7cfa618895a999a0de763a541d730775d0d5f"},
|
||||
{file = "coverage-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3edbb3ec580c73e5a264f5d04f30245bc98eff1a26765d46c5c65134f0a0e2f7"},
|
||||
{file = "coverage-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea452a2d83964d08232ade470091015e7ab9b8f53acbec10f2210fbab4ce7e43"},
|
||||
{file = "coverage-6.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1770d24f45f1f2daeae34cfa3b33fcb29702153544cd2ad40d58399dd4ff53b5"},
|
||||
{file = "coverage-6.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ad7182a82843f9f85487f44567c8c688f16c906bdb8d0e44ae462aed61cb8f1b"},
|
||||
{file = "coverage-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:9d242a2434801ef5125330deddb4cddba8990c9a49b3dec99dca17dd7eefba5a"},
|
||||
{file = "coverage-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e66c50f0ab445fec920a9f084914ea1776a809e3016c3738519048195f851bbb"},
|
||||
{file = "coverage-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5b1ceacb86e0a9558061dcc6baae865ed25933ea57effea644f21657cdce19bc"},
|
||||
{file = "coverage-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e15ab5afbee34abf716fece80ea33ea09a82e7450512f022723b1a82ec9a4e"},
|
||||
{file = "coverage-6.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6873f3f954d3e3ab8b1881f4e5307cc19f70c9f931c41048d9f7e6fd946eabe7"},
|
||||
{file = "coverage-6.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0898d6948b31df13391cd40568de8f35fa5901bc922c5ae05cf070587cb9c666"},
|
||||
{file = "coverage-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9c416ba03844608f45661a5b48dc59c6b5e89956efe388564dd138ca8caf540b"},
|
||||
{file = "coverage-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:66fe33e9e0df58675e08e83fe257f89e7f625e7633ea93d0872154e09cce2724"},
|
||||
{file = "coverage-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:353a50f123f0185cdb7a1e1e3e2cfb9d1fd7e293cfaf68eedaf5bd8e02e3ec32"},
|
||||
{file = "coverage-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b81a4e667c45b13658b84f9b8f1d32ef86d5405fabcbd181b76b9e51d295f397"},
|
||||
{file = "coverage-6.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:17426808e8e0824f864876312d41961223bf5e503bf8f1f846735279a60ea345"},
|
||||
{file = "coverage-6.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e11cca9eb5c9b3eaad899728ee2ce916138399ee8cbbccaadc1871fecb750827"},
|
||||
{file = "coverage-6.0.1-cp38-cp38-win32.whl", hash = "sha256:0a7e55cc9f7efa22d5cc9966276ec7a40a8803676f6ccbfdc06a486fba9aa9ee"},
|
||||
{file = "coverage-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:4eb9cd910ca8e243f930243a9940ea1a522e32435d15668445753d087c30ee12"},
|
||||
{file = "coverage-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:83682b73785d2e078e0b5f63410b8125b122e1a22422640c57edd4011c950f3e"},
|
||||
{file = "coverage-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b45f89a8ef65c29195f8f28dbe215f44ccb29d934f3e862d2a5c12e38698a793"},
|
||||
{file = "coverage-6.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:73880a80fad0597eca43e213e5e1711bf6c0fcdb7eb6b01b3b17841ebe5a7f8d"},
|
||||
{file = "coverage-6.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f398d38e6ebc2637863db1d7be3d4f9c5174e7d24bb3b0716cdb1f204669cbcf"},
|
||||
{file = "coverage-6.0.1-cp39-cp39-win32.whl", hash = "sha256:1864bdf9b2ccb43e724051bc23a1c558daf101ad4488ede1945f2a8be1facdad"},
|
||||
{file = "coverage-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:c9c413c4397d4cdc7ca89286158d240ce524f9667b52c9a64dd7e13d16cf8815"},
|
||||
{file = "coverage-6.0.1-pp36-none-any.whl", hash = "sha256:65da6e3e8325291f012921bbf71fea0a97824e1c573981871096aac6e2cf0ec5"},
|
||||
{file = "coverage-6.0.1-pp37-none-any.whl", hash = "sha256:07efe1fbd72e67df026ad5109bcd216acbbd4a29d5208b3dab61779bae6b7b26"},
|
||||
{file = "coverage-6.0.1.tar.gz", hash = "sha256:3490ff6dbf3f7accf0750136ed60ae1f487bccc1f097740e3b21262bc9c89854"},
|
||||
{file = "coverage-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1549e1d08ce38259de2bc3e9a0d5f3642ff4a8f500ffc1b2df73fd621a6cdfc0"},
|
||||
{file = "coverage-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcae10fccb27ca2a5f456bf64d84110a5a74144be3136a5e598f9d9fb48c0caa"},
|
||||
{file = "coverage-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:53a294dc53cfb39c74758edaa6305193fb4258a30b1f6af24b360a6c8bd0ffa7"},
|
||||
{file = "coverage-6.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8251b37be1f2cd9c0e5ccd9ae0380909c24d2a5ed2162a41fcdbafaf59a85ebd"},
|
||||
{file = "coverage-6.0.2-cp310-cp310-win32.whl", hash = "sha256:db42baa892cba723326284490283a68d4de516bfb5aaba369b4e3b2787a778b7"},
|
||||
{file = "coverage-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:bbffde2a68398682623d9dd8c0ca3f46fda074709b26fcf08ae7a4c431a6ab2d"},
|
||||
{file = "coverage-6.0.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:60e51a3dd55540bec686d7fff61b05048ca31e804c1f32cbb44533e6372d9cc3"},
|
||||
{file = "coverage-6.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a6a9409223a27d5ef3cca57dd7cd4dfcb64aadf2fad5c3b787830ac9223e01a"},
|
||||
{file = "coverage-6.0.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4b34ae4f51bbfa5f96b758b55a163d502be3dcb24f505d0227858c2b3f94f5b9"},
|
||||
{file = "coverage-6.0.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3bbda1b550e70fa6ac40533d3f23acd4f4e9cb4e6e77251ce77fdf41b3309fb2"},
|
||||
{file = "coverage-6.0.2-cp36-cp36m-win32.whl", hash = "sha256:4e28d2a195c533b58fc94a12826f4431726d8eb029ac21d874345f943530c122"},
|
||||
{file = "coverage-6.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a82d79586a0a4f5fd1cf153e647464ced402938fbccb3ffc358c7babd4da1dd9"},
|
||||
{file = "coverage-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3be1206dc09fb6298de3fce70593e27436862331a85daee36270b6d0e1c251c4"},
|
||||
{file = "coverage-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9cd3828bbe1a40070c11fe16a51df733fd2f0cb0d745fb83b7b5c1f05967df7"},
|
||||
{file = "coverage-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d036dc1ed8e1388e995833c62325df3f996675779541f682677efc6af71e96cc"},
|
||||
{file = "coverage-6.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04560539c19ec26995ecfb3d9307ff154fbb9a172cb57e3b3cfc4ced673103d1"},
|
||||
{file = "coverage-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:e4fb7ced4d9dec77d6cf533acfbf8e1415fe799430366affb18d69ee8a3c6330"},
|
||||
{file = "coverage-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:77b1da5767ed2f44611bc9bc019bc93c03fa495728ec389759b6e9e5039ac6b1"},
|
||||
{file = "coverage-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:61b598cbdbaae22d9e34e3f675997194342f866bb1d781da5d0be54783dce1ff"},
|
||||
{file = "coverage-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36e9040a43d2017f2787b28d365a4bb33fcd792c7ff46a047a04094dc0e2a30d"},
|
||||
{file = "coverage-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9f1627e162e3864a596486774876415a7410021f4b67fd2d9efdf93ade681afc"},
|
||||
{file = "coverage-6.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e7a0b42db2a47ecb488cde14e0f6c7679a2c5a9f44814393b162ff6397fcdfbb"},
|
||||
{file = "coverage-6.0.2-cp38-cp38-win32.whl", hash = "sha256:a1b73c7c4d2a42b9d37dd43199c5711d91424ff3c6c22681bc132db4a4afec6f"},
|
||||
{file = "coverage-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:1db67c497688fd4ba85b373b37cc52c50d437fd7267520ecd77bddbd89ea22c9"},
|
||||
{file = "coverage-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f2f184bf38e74f152eed7f87e345b51f3ab0b703842f447c22efe35e59942c24"},
|
||||
{file = "coverage-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1cf1deb3d5544bd942356364a2fdc8959bad2b6cf6eb17f47d301ea34ae822"},
|
||||
{file = "coverage-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad9b8c1206ae41d46ec7380b78ba735ebb77758a650643e841dd3894966c31d0"},
|
||||
{file = "coverage-6.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:381d773d896cc7f8ba4ff3b92dee4ed740fb88dfe33b6e42efc5e8ab6dfa1cfe"},
|
||||
{file = "coverage-6.0.2-cp39-cp39-win32.whl", hash = "sha256:424c44f65e8be58b54e2b0bd1515e434b940679624b1b72726147cfc6a9fc7ce"},
|
||||
{file = "coverage-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:abbff240f77347d17306d3201e14431519bf64495648ca5a49571f988f88dee9"},
|
||||
{file = "coverage-6.0.2-pp36-none-any.whl", hash = "sha256:7092eab374346121805fb637572483270324407bf150c30a3b161fc0c4ca5164"},
|
||||
{file = "coverage-6.0.2-pp37-none-any.whl", hash = "sha256:30922626ce6f7a5a30bdba984ad21021529d3d05a68b4f71ea3b16bda35b8895"},
|
||||
{file = "coverage-6.0.2.tar.gz", hash = "sha256:6807947a09510dc31fa86f43595bf3a14017cd60bf633cc746d52141bfa6b149"},
|
||||
]
|
||||
cryptography = [
|
||||
{file = "cryptography-35.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9"},
|
||||
@ -1651,6 +1775,10 @@ deprecated = [
|
||||
{file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"},
|
||||
{file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"},
|
||||
]
|
||||
distlib = [
|
||||
{file = "distlib-0.3.3-py2.py3-none-any.whl", hash = "sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31"},
|
||||
{file = "distlib-0.3.3.zip", hash = "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05"},
|
||||
]
|
||||
docspec = [
|
||||
{file = "docspec-1.2.0-py3-none-any.whl", hash = "sha256:df3d2d014e0a77ac0997c9052102cf2262f12640c87af3782e2310589be4bb4c"},
|
||||
{file = "docspec-1.2.0.tar.gz", hash = "sha256:5206c061d2c0171add8412028a79b436acc87786cfc582aeda341beda81ae582"},
|
||||
@ -1663,6 +1791,10 @@ fastapi = [
|
||||
{file = "fastapi-0.70.0-py3-none-any.whl", hash = "sha256:a36d5f2fad931aa3575c07a3472c784e81f3e664e3bb5c8b9c88d0ec1104f59c"},
|
||||
{file = "fastapi-0.70.0.tar.gz", hash = "sha256:66da43cfe5185ea1df99552acffd201f1832c6b364e0f4136c0a99f933466ced"},
|
||||
]
|
||||
filelock = [
|
||||
{file = "filelock-3.3.0-py3-none-any.whl", hash = "sha256:bbc6a0382fe8ec4744ecdf6683a2e07f65eb10ff1aff53fc02a202565446cde0"},
|
||||
{file = "filelock-3.3.0.tar.gz", hash = "sha256:8c7eab13dc442dc249e95158bcc12dec724465919bdc9831fdbf0660f03d1785"},
|
||||
]
|
||||
flake8 = [
|
||||
{file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
|
||||
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
|
||||
@ -1767,9 +1899,13 @@ greenlet = [
|
||||
{file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"},
|
||||
{file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"},
|
||||
]
|
||||
identify = [
|
||||
{file = "identify-2.3.0-py2.py3-none-any.whl", hash = "sha256:d1e82c83d063571bb88087676f81261a4eae913c492dafde184067c584bc7c05"},
|
||||
{file = "identify-2.3.0.tar.gz", hash = "sha256:fd08c97f23ceee72784081f1ce5125c8f53a02d3f2716dde79a6ab8f1039fea5"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
|
||||
{file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
|
||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
]
|
||||
immutables = [
|
||||
{file = "immutables-0.16-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:acbfa79d44228d96296279068441f980dc63dbed52522d9227ff9f4d96c6627e"},
|
||||
@ -1804,6 +1940,10 @@ importlib-metadata = [
|
||||
{file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
|
||||
{file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
|
||||
]
|
||||
importlib-resources = [
|
||||
{file = "importlib_resources-5.2.2-py3-none-any.whl", hash = "sha256:2480d8e07d1890056cb53c96e3de44fead9c62f2ba949b0f2e4c4345f4afa977"},
|
||||
{file = "importlib_resources-5.2.2.tar.gz", hash = "sha256:a65882a4d0fe5fbf702273456ba2ce74fe44892c25e42e057aca526b702a6d4b"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
@ -1931,6 +2071,10 @@ mysqlclient = [
|
||||
{file = "mysqlclient-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:fc575093cf81b6605bed84653e48b277318b880dc9becf42dd47fa11ffd3e2b6"},
|
||||
{file = "mysqlclient-2.0.3.tar.gz", hash = "sha256:f6ebea7c008f155baeefe16c56cd3ee6239f7a5a9ae42396c2f1860f08a7c432"},
|
||||
]
|
||||
nodeenv = [
|
||||
{file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
|
||||
{file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
|
||||
]
|
||||
"nr.fs" = [
|
||||
{file = "nr.fs-1.6.3-py2.py3-none-any.whl", hash = "sha256:64108c168ea2e8077fdf5f0c5417459d1a145fe34cb305fe90faeb75b4e8b421"},
|
||||
{file = "nr.fs-1.6.3.tar.gz", hash = "sha256:788aa0a04c4143f95c5245bc8ccc0c0872e932be533bd37780fbb55afcdf124a"},
|
||||
@ -2002,13 +2146,12 @@ pluggy = [
|
||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||
]
|
||||
pre-commit = [
|
||||
{file = "pre_commit-2.15.0-py2.py3-none-any.whl", hash = "sha256:a4ed01000afcb484d9eb8d504272e642c4c4099bbad3a6b27e519bd6a3e928a6"},
|
||||
{file = "pre_commit-2.15.0.tar.gz", hash = "sha256:3c25add78dbdfb6a28a651780d5c311ac40dd17f160eb3954a0c59da40a505a7"},
|
||||
]
|
||||
psycopg2-binary = [
|
||||
{file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"},
|
||||
{file = "psycopg2_binary-2.9.1-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:24b0b6688b9f31a911f2361fe818492650795c9e5d3a1bc647acbd7440142a4f"},
|
||||
{file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:542875f62bc56e91c6eac05a0deadeae20e1730be4c6334d8f04c944fcd99759"},
|
||||
{file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661509f51531ec125e52357a489ea3806640d0ca37d9dada461ffc69ee1e7b6e"},
|
||||
{file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:d92272c7c16e105788efe2cfa5d680f07e34e0c29b03c1908f8636f55d5f915a"},
|
||||
{file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:736b8797b58febabb85494142c627bd182b50d2a7ec65322983e71065ad3034c"},
|
||||
{file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"},
|
||||
{file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"},
|
||||
{file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"},
|
||||
@ -2345,6 +2488,10 @@ urllib3 = [
|
||||
{file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"},
|
||||
{file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"},
|
||||
]
|
||||
virtualenv = [
|
||||
{file = "virtualenv-20.8.1-py2.py3-none-any.whl", hash = "sha256:10062e34c204b5e4ec5f62e6ef2473f8ba76513a9a617e873f1f8fb4a519d300"},
|
||||
{file = "virtualenv-20.8.1.tar.gz", hash = "sha256:bcc17f0b3a29670dd777d6f0755a4c04f28815395bca279cdcb213b97199a6b8"},
|
||||
]
|
||||
watchdog = [
|
||||
{file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"},
|
||||
{file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"},
|
||||
@ -2371,51 +2518,50 @@ watchdog = [
|
||||
{file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"},
|
||||
]
|
||||
wrapt = [
|
||||
{file = "wrapt-1.13.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:97f016514ceac524832e7d1bd41cf928b992ebe0324d59736f84ad5f4bbe0632"},
|
||||
{file = "wrapt-1.13.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:0b2cbe418beeff3aadb3afc39a67d3f5f6a3eb020ceb5f2bcf56bef14b33629a"},
|
||||
{file = "wrapt-1.13.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:95c9fcfc326fdd3e2fd264e808f6474ca7ffd253feb3a505ee5ceb4d78216ef7"},
|
||||
{file = "wrapt-1.13.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:db0daf2afca9f3b3a76e96ecb5f55ba82615ec584471d7aa27c1bdeb9e3888bb"},
|
||||
{file = "wrapt-1.13.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1b46e4fe0f9efbfaf1ee82fc79f9cb044c69b67b181c58370440d396fe40736e"},
|
||||
{file = "wrapt-1.13.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b0eed9b295039a619f64667f27cffbffcfc0559073d562700912ca6266bc8b28"},
|
||||
{file = "wrapt-1.13.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8a6ba1b00d07f5a90a2d2eb1804a42e2067a6145b7745a8297664a75a8a232ba"},
|
||||
{file = "wrapt-1.13.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:947a8d9d7829364e11eca88af18394713c8f98571cbc672b12545977d837f054"},
|
||||
{file = "wrapt-1.13.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6aa687da5565674c9696fafd2b8d44a04fb697ec2431af21c3def9cbedc4082a"},
|
||||
{file = "wrapt-1.13.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:7929ce97be2f7c49f454a6f8e014225e53cc3767fe48cce94b188de2225232ac"},
|
||||
{file = "wrapt-1.13.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2d18618440df6bc072762625e9c843d32a7328347c321b89f8df3a7c4a72ce6c"},
|
||||
{file = "wrapt-1.13.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:cb0b12b365b054bee2a53078a67df81781be0686cc3f3ab8bbdd16b2e188570a"},
|
||||
{file = "wrapt-1.13.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:3816922f0941f1637869a04e25d1e5261dfa55cc6b39c73872cbf192ea562443"},
|
||||
{file = "wrapt-1.13.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:b41ce8ee3825634e67883dd4dab336f95d0cc9d223fb7e224dcd36d66af93694"},
|
||||
{file = "wrapt-1.13.1-cp35-cp35m-win32.whl", hash = "sha256:d0ae90fd60c7473e437b0dd48ae323c11f631fe47c243056f9e7505d26e8e2f6"},
|
||||
{file = "wrapt-1.13.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f4377eda306b488255ea4336662cd9015a902d6dc2ed77a3e4c1e3b42387453a"},
|
||||
{file = "wrapt-1.13.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:bc42803987eb46b5fc67ec9a072df15a72ee9db61e3b7dd955d82581bf141f60"},
|
||||
{file = "wrapt-1.13.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:04a00cef5d1b9e0e8db997816437b436e859106283c4771a40c4de4759344765"},
|
||||
{file = "wrapt-1.13.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:836c73f53a0cefc7ba10c6f4a0d78894cb4876f56035fe500b029e0a1ae0ffe9"},
|
||||
{file = "wrapt-1.13.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:6c241b4ef0744590ae0ee89305743977e478200cff961bdcc6b3d0530aea3377"},
|
||||
{file = "wrapt-1.13.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:19b2c992668c9ca764899bae52987a04041ebc21859d2646db0b27e089c2fd6b"},
|
||||
{file = "wrapt-1.13.1-cp36-cp36m-win32.whl", hash = "sha256:9d200716eb4bb1d73f47f3ccc4f98fdf979dcc82d752183828f1be2e332b6874"},
|
||||
{file = "wrapt-1.13.1-cp36-cp36m-win_amd64.whl", hash = "sha256:77fef0bfdc612f5f30e43392a9f67dddaf4f48f299421bf25f910d0f47173f3d"},
|
||||
{file = "wrapt-1.13.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1137e6aef3ac267c2af7d3af0266ef3f8dd1e5cde67b8eac9fa3b94e7fa0ada"},
|
||||
{file = "wrapt-1.13.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:972099fa9cf4e43c255701c78ec5098c2fec4d6ea669a110b3414a158e772b0a"},
|
||||
{file = "wrapt-1.13.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5dc6c8cfaf4ff2a4632f8f97d29f555d6951eb0f905d3d47b3fd69bddb653214"},
|
||||
{file = "wrapt-1.13.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:f1e2cea943192e24070b65bda862901c02bdf7c6abcd66ef5381ad6511921067"},
|
||||
{file = "wrapt-1.13.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8a184c655bb41295a9b0c28745a1b762c0c86025e43808b7e814f9cedc6c563d"},
|
||||
{file = "wrapt-1.13.1-cp37-cp37m-win32.whl", hash = "sha256:6b81913fdba96e286f0c6007eb61f0158e64a1941bfc72fee61b34a4f8f9877f"},
|
||||
{file = "wrapt-1.13.1-cp37-cp37m-win_amd64.whl", hash = "sha256:aa637733f1d599077522f6a1f0c6c40389aa90a44cba37afcefef26f8e53d28f"},
|
||||
{file = "wrapt-1.13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec803c9d6e4ce037201132d903ff8b0dd26c9688be50ce4c77c420c076e78ff7"},
|
||||
{file = "wrapt-1.13.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8055f8cc9a80dc1db01f31af6399b83f597ec164f07b7251d2a1bf1c6c025190"},
|
||||
{file = "wrapt-1.13.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3658ae9c704906cab5865a00c1aa9e1fd3555074d1a4462fa1742d7fea8260ae"},
|
||||
{file = "wrapt-1.13.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:9f839c47698052ef5c2c094e21f8a06d0828aebe52d20cdb505faa318c62e886"},
|
||||
{file = "wrapt-1.13.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:fd5320bf61a2e8d3b46d9e183323293c9a695df8f38c98d17c45e1846758f9a9"},
|
||||
{file = "wrapt-1.13.1-cp38-cp38-win32.whl", hash = "sha256:e2eb4f38441b56698b4d40d48fd331e4e8a0477264785d08cbced63813d4bd29"},
|
||||
{file = "wrapt-1.13.1-cp38-cp38-win_amd64.whl", hash = "sha256:2f6fbea8936ba862425664fc689182a8ef50a6d88cd49f3cd073eccd3e78c930"},
|
||||
{file = "wrapt-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4f3f99bb8eed5d394bbb898c5191ed91ebf21187d52b2c45895733ae2798f373"},
|
||||
{file = "wrapt-1.13.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:21c1710f61aa95b4be83a32b6d6facbb0efdfac22dee65e1caa72a83deed7cda"},
|
||||
{file = "wrapt-1.13.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:40fd2cebad4010787de4221ec27a650635eed3e49e4bbfa8244fc34836cc2457"},
|
||||
{file = "wrapt-1.13.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:c803526c0d3fa426e06de379b4eb56102234f2dc3c3a24a500d7962a83ca6166"},
|
||||
{file = "wrapt-1.13.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:e5a0727ea56de6e9a17693589bcf913d6bf1ec49f12d4671993321f3325fda4f"},
|
||||
{file = "wrapt-1.13.1-cp39-cp39-win32.whl", hash = "sha256:04312fbf51e9dd15261228e6b4bed0c0ed5723ccf986645d2c7308511dccba35"},
|
||||
{file = "wrapt-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:bd705e341baccc3d1ef20e790b1f6383bd4ae92a77ba87a86ece8189fab8793c"},
|
||||
{file = "wrapt-1.13.1.tar.gz", hash = "sha256:909a80ce028821c7ad01bdcaa588126825931d177cdccd00b3545818d4a195ce"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3de7b4d3066cc610054e7aa2c005645e308df2f92be730aae3a47d42e910566a"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:8164069f775c698d15582bf6320a4f308c50d048c1c10cf7d7a341feaccf5df7"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9adee1891253670575028279de8365c3a02d3489a74a66d774c321472939a0b1"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a70d876c9aba12d3bd7f8f1b05b419322c6789beb717044eea2c8690d35cb91b"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3f87042623530bcffea038f824b63084180513c21e2e977291a9a7e65a66f13b"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e634136f700a21e1fcead0c137f433dde928979538c14907640607d43537d468"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3e33c138d1e3620b1e0cc6fd21e46c266393ed5dae0d595b7ed5a6b73ed57aa0"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:283e402e5357e104ac1e3fba5791220648e9af6fb14ad7d9cc059091af2b31d2"},
|
||||
{file = "wrapt-1.13.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ccb34ce599cab7f36a4c90318697ead18312c67a9a76327b3f4f902af8f68ea1"},
|
||||
{file = "wrapt-1.13.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:fbad5ba74c46517e6488149514b2e2348d40df88cd6b52a83855b7a8bf04723f"},
|
||||
{file = "wrapt-1.13.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:724ed2bc9c91a2b9026e5adce310fa60c6e7c8760b03391445730b9789b9d108"},
|
||||
{file = "wrapt-1.13.2-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:83f2793ec6f3ef513ad8d5b9586f5ee6081cad132e6eae2ecb7eac1cc3decae0"},
|
||||
{file = "wrapt-1.13.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:0473d1558b93e314e84313cc611f6c86be779369f9d3734302bf185a4d2625b1"},
|
||||
{file = "wrapt-1.13.2-cp35-cp35m-win32.whl", hash = "sha256:15eee0e6fd07f48af2f66d0e6f2ff1916ffe9732d464d5e2390695296872cad9"},
|
||||
{file = "wrapt-1.13.2-cp35-cp35m-win_amd64.whl", hash = "sha256:bc85d17d90201afd88e3d25421da805e4e135012b5d1f149e4de2981394b2a52"},
|
||||
{file = "wrapt-1.13.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6ee5f8734820c21b9b8bf705e99faba87f21566d20626568eeb0d62cbeaf23c"},
|
||||
{file = "wrapt-1.13.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:53c6706a1bcfb6436f1625511b95b812798a6d2ccc51359cd791e33722b5ea32"},
|
||||
{file = "wrapt-1.13.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fbe6aebc9559fed7ea27de51c2bf5c25ba2a4156cf0017556f72883f2496ee9a"},
|
||||
{file = "wrapt-1.13.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:0582180566e7a13030f896c2f1ac6a56134ab5f3c3f4c5538086f758b1caf3f2"},
|
||||
{file = "wrapt-1.13.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bff0a59387a0a2951cb869251257b6553663329a1b5525b5226cab8c88dcbe7e"},
|
||||
{file = "wrapt-1.13.2-cp36-cp36m-win32.whl", hash = "sha256:df3eae297a5f1594d1feb790338120f717dac1fa7d6feed7b411f87e0f2401c7"},
|
||||
{file = "wrapt-1.13.2-cp36-cp36m-win_amd64.whl", hash = "sha256:1eb657ed84f4d3e6ad648483c8a80a0cf0a78922ef94caa87d327e2e1ad49b48"},
|
||||
{file = "wrapt-1.13.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0cdedf681db878416c05e1831ec69691b0e6577ac7dca9d4f815632e3549580"},
|
||||
{file = "wrapt-1.13.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:87ee3c73bdfb4367b26c57259995935501829f00c7b3eed373e2ad19ec21e4e4"},
|
||||
{file = "wrapt-1.13.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3e0d16eedc242d01a6f8cf0623e9cdc3b869329da3f97a15961d8864111d8cf0"},
|
||||
{file = "wrapt-1.13.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:8318088860968c07e741537030b1abdd8908ee2c71fbe4facdaade624a09e006"},
|
||||
{file = "wrapt-1.13.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d90520616fce71c05dedeac3a0fe9991605f0acacd276e5f821842e454485a70"},
|
||||
{file = "wrapt-1.13.2-cp37-cp37m-win32.whl", hash = "sha256:22142afab65daffc95863d78effcbd31c19a8003eca73de59f321ee77f73cadb"},
|
||||
{file = "wrapt-1.13.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d0d717e10f952df7ea41200c507cc7e24458f4c45b56c36ad418d2e79dacd1d4"},
|
||||
{file = "wrapt-1.13.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:593cb049ce1c391e0288523b30426c4430b26e74c7e6f6e2844bd99ac7ecc831"},
|
||||
{file = "wrapt-1.13.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8860c8011a6961a651b1b9f46fdbc589ab63b0a50d645f7d92659618a3655867"},
|
||||
{file = "wrapt-1.13.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ada5e29e59e2feb710589ca1c79fd989b1dd94d27079dc1d199ec954a6ecc724"},
|
||||
{file = "wrapt-1.13.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:fdede980273aeca591ad354608778365a3a310e0ecdd7a3587b38bc5be9b1808"},
|
||||
{file = "wrapt-1.13.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:af9480de8e63c5f959a092047aaf3d7077422ded84695b3398f5d49254af3e90"},
|
||||
{file = "wrapt-1.13.2-cp38-cp38-win32.whl", hash = "sha256:c65e623ea7556e39c4f0818200a046cbba7575a6b570ff36122c276fdd30ab0a"},
|
||||
{file = "wrapt-1.13.2-cp38-cp38-win_amd64.whl", hash = "sha256:b20703356cae1799080d0ad15085dc3213c1ac3f45e95afb9f12769b98231528"},
|
||||
{file = "wrapt-1.13.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c5c4cf188b5643a97e87e2110bbd4f5bc491d54a5b90633837b34d5df6a03fe"},
|
||||
{file = "wrapt-1.13.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:82223f72eba6f63eafca87a0f614495ae5aa0126fe54947e2b8c023969e9f2d7"},
|
||||
{file = "wrapt-1.13.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:81a4cf257263b299263472d669692785f9c647e7dca01c18286b8f116dbf6b38"},
|
||||
{file = "wrapt-1.13.2-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:728e2d9b7a99dd955d3426f237b940fc74017c4a39b125fec913f575619ddfe9"},
|
||||
{file = "wrapt-1.13.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:7574de567dcd4858a2ffdf403088d6df8738b0e1eabea220553abf7c9048f59e"},
|
||||
{file = "wrapt-1.13.2-cp39-cp39-win32.whl", hash = "sha256:c7ac2c7a8e34bd06710605b21dd1f3576764443d68e069d2afba9b116014d072"},
|
||||
{file = "wrapt-1.13.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e6d1a8eeef415d7fb29fe017de0e48f45e45efd2d1bfda28fc50b7b330859ef"},
|
||||
{file = "wrapt-1.13.2.tar.gz", hash = "sha256:dca56cc5963a5fd7c2aa8607017753f534ee514e09103a6c55d2db70b50e7447"},
|
||||
]
|
||||
yappi = [
|
||||
{file = "yappi-1.3.3.tar.gz", hash = "sha256:855890cd9a90d833dd2df632d648de8ccd0a4c3131f1edc8abd004db0625b5e8"},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "ormar"
|
||||
version = "0.10.20"
|
||||
version = "0.10.21"
|
||||
description = "A simple async ORM with fastapi in mind and pydantic validation."
|
||||
authors = ["Radosław Drążkiewicz <collerek@gmail.com>"]
|
||||
license = "MIT"
|
||||
@ -115,6 +115,8 @@ dataclasses = { version = ">=0.6.0,<0.8 || >0.8,<1.0.0" }
|
||||
# Performance testing
|
||||
yappi = "^1.3.3"
|
||||
|
||||
pre-commit = "^2.15.0"
|
||||
|
||||
[tool.poetry.extras]
|
||||
postgresql = ["asyncpg", "psycopg2-binary"]
|
||||
postgres = ["asyncpg", "psycopg2-binary"]
|
||||
|
||||
@ -121,7 +121,6 @@ def test_all_endpoints():
|
||||
"blob_col": blob.decode("utf-8"),
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
item = Organisation(**response.json())
|
||||
assert item.pk is not None
|
||||
|
||||
@ -4,11 +4,8 @@ from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from tests.settings import DATABASE_URL
|
||||
from tests.test_inheritance_and_pydantic_generation.test_geting_the_pydantic_models import (
|
||||
from tests.test_inheritance_and_pydantic_generation.test_geting_pydantic_models import (
|
||||
Category,
|
||||
Item,
|
||||
MutualA,
|
||||
MutualB,
|
||||
SelfRef,
|
||||
database,
|
||||
metadata,
|
||||
@ -53,7 +50,9 @@ app.post("/categories/", response_model=Category)(create_category)
|
||||
response_model=SelfRef.get_pydantic(exclude={"parent", "children__name"}),
|
||||
)
|
||||
async def create_selfref(
|
||||
selfref: SelfRef.get_pydantic(exclude={"children__name"}), # type: ignore
|
||||
selfref: SelfRef.get_pydantic( # type: ignore
|
||||
exclude={"children__name"} # noqa: F821
|
||||
),
|
||||
):
|
||||
selfr = SelfRef(**selfref.dict())
|
||||
await selfr.save()
|
||||
|
||||
@ -14,7 +14,7 @@ class BaseMeta(ormar.ModelMeta):
|
||||
metadata = metadata
|
||||
|
||||
|
||||
class TestModel(ormar.Model):
|
||||
class NewTestModel(ormar.Model):
|
||||
class Meta:
|
||||
database = database
|
||||
metadata = metadata
|
||||
@ -37,5 +37,5 @@ def create_test_database():
|
||||
|
||||
|
||||
def test_model_field_order():
|
||||
TestCreate = TestModel.get_pydantic(exclude={"a"})
|
||||
TestCreate = NewTestModel.get_pydantic(exclude={"a"})
|
||||
assert list(TestCreate.__fields__.keys()) == ["b", "c", "d", "e", "f"]
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
import enum
|
||||
|
||||
import databases
|
||||
import pydantic
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from pydantic import ValidationError
|
||||
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
metadata = sqlalchemy.MetaData()
|
||||
database = databases.Database(DATABASE_URL)
|
||||
|
||||
|
||||
class BaseMeta(ormar.ModelMeta):
|
||||
database = database
|
||||
metadata = metadata
|
||||
|
||||
|
||||
class EnumExample(str, enum.Enum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
C = "C"
|
||||
|
||||
|
||||
class ModelExample(ormar.Model):
|
||||
class Meta(ormar.ModelMeta):
|
||||
database = database
|
||||
metadata = metadata
|
||||
tablename = "examples"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
str_field: str = ormar.String(min_length=5, max_length=10, nullable=False)
|
||||
enum_field: str = ormar.String(
|
||||
max_length=1, nullable=False, choices=list(EnumExample)
|
||||
)
|
||||
|
||||
@pydantic.validator("str_field")
|
||||
def validate_str_field(cls, v):
|
||||
if " " not in v:
|
||||
raise ValueError("must contain a space")
|
||||
return v
|
||||
|
||||
|
||||
ModelExampleCreate = ModelExample.get_pydantic(exclude={"id"})
|
||||
|
||||
|
||||
def test_ormar_validator():
|
||||
ModelExample(str_field="a aaaaaa", enum_field="A")
|
||||
with pytest.raises(ValidationError) as e:
|
||||
ModelExample(str_field="aaaaaaa", enum_field="A")
|
||||
assert "must contain a space" in str(e)
|
||||
with pytest.raises(ValidationError) as e:
|
||||
ModelExample(str_field="a aaaaaaa", enum_field="Z")
|
||||
assert "not in allowed choices" in str(e)
|
||||
|
||||
|
||||
def test_pydantic_validator():
|
||||
ModelExampleCreate(str_field="a aaaaaa", enum_field="A")
|
||||
with pytest.raises(ValidationError) as e:
|
||||
ModelExampleCreate(str_field="aaaaaaa", enum_field="A")
|
||||
assert "must contain a space" in str(e)
|
||||
with pytest.raises(ValidationError) as e:
|
||||
ModelExampleCreate(str_field="a aaaaaaa", enum_field="Z")
|
||||
assert "not in allowed choices" in str(e)
|
||||
87
tests/test_model_definition/test_model_construct.py
Normal file
87
tests/test_model_definition/test_model_construct.py
Normal file
@ -0,0 +1,87 @@
|
||||
from typing import List
|
||||
|
||||
import databases
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class NickNames(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "nicks"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100, nullable=False, name="hq_name")
|
||||
|
||||
|
||||
class NicksHq(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "nicks_x_hq"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class HQ(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "hqs"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100, nullable=False, name="hq_name")
|
||||
nicks: List[NickNames] = ormar.ManyToMany(NickNames, through=NicksHq)
|
||||
|
||||
|
||||
class Company(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "companies"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100, nullable=False, name="company_name")
|
||||
founded: int = ormar.Integer(nullable=True)
|
||||
hq: HQ = ormar.ForeignKey(HQ)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.drop_all(engine)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_init_and_construct_has_same_effect():
|
||||
async with database:
|
||||
async with database.transaction(force_rollback=True):
|
||||
hq = await HQ.objects.create(name="Main")
|
||||
comp = Company(name="Banzai", hq=hq, founded=1988)
|
||||
comp2 = Company.construct(**dict(name="Banzai", hq=hq, founded=1988))
|
||||
assert comp.dict() == comp2.dict()
|
||||
|
||||
comp3 = Company.construct(**dict(name="Banzai", hq=hq.dict(), founded=1988))
|
||||
assert comp.dict() == comp3.dict()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_init_and_construct_has_same_effect_with_m2m():
|
||||
async with database:
|
||||
async with database.transaction(force_rollback=True):
|
||||
n1 = await NickNames(name="test").save()
|
||||
n2 = await NickNames(name="test2").save()
|
||||
hq = HQ(name="Main", nicks=[n1, n2])
|
||||
hq2 = HQ.construct(**dict(name="Main", nicks=[n1, n2]))
|
||||
assert hq.dict() == hq2.dict()
|
||||
|
||||
hq3 = HQ.construct(**dict(name="Main", nicks=[n1.dict(), n2.dict()]))
|
||||
assert hq.dict() == hq3.dict()
|
||||
@ -63,7 +63,7 @@ def create_test_database():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_instantation_false_save_true():
|
||||
async def test_instantiation_false_save_true():
|
||||
async with database:
|
||||
async with database.transaction(force_rollback=True):
|
||||
comp = Company(name="Banzai", founded=1988)
|
||||
|
||||
Reference in New Issue
Block a user