mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
Refactor PrivateAttr to type-check like Field (#2057)
* Refactor PrivateAttr to type-check like Field * Change TypeError to ValueError for consistency * Add PrivateAttr example to mypy tests
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Fix mypy assignment error when using `PrivateAttr`
|
||||
+26
-12
@@ -799,21 +799,10 @@ class ModelField(Representation):
|
||||
return args
|
||||
|
||||
|
||||
class PrivateAttr(Representation):
|
||||
"""
|
||||
Indicates that attribute is only used internally and never mixed with regular fields.
|
||||
|
||||
Types or values of private attrs are not checked by pydantic and it's up to you to keep them relevant.
|
||||
|
||||
Private attrs are stored in model __slots__.
|
||||
"""
|
||||
|
||||
class ModelPrivateAttr(Representation):
|
||||
__slots__ = ('default', 'default_factory')
|
||||
|
||||
def __init__(self, default: Any = Undefined, *, default_factory: Optional[NoArgAnyCallable] = None) -> None:
|
||||
if default is not Undefined and default_factory is not None:
|
||||
raise TypeError('default and default_factory args can not be used together')
|
||||
|
||||
self.default = default
|
||||
self.default_factory = default_factory
|
||||
|
||||
@@ -825,3 +814,28 @@ class PrivateAttr(Representation):
|
||||
other.default,
|
||||
other.default_factory,
|
||||
)
|
||||
|
||||
|
||||
def PrivateAttr(
|
||||
default: Any = Undefined,
|
||||
*,
|
||||
default_factory: Optional[NoArgAnyCallable] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Indicates that attribute is only used internally and never mixed with regular fields.
|
||||
|
||||
Types or values of private attrs are not checked by pydantic and it's up to you to keep them relevant.
|
||||
|
||||
Private attrs are stored in model __slots__.
|
||||
|
||||
:param default: the attribute’s default value
|
||||
:param default_factory: callable that will be called when a default value is needed for this attribute
|
||||
If both `default` and `default_factory` are set, an error is raised.
|
||||
"""
|
||||
if default is not Undefined and default_factory is not None:
|
||||
raise ValueError('cannot specify both default and default_factory')
|
||||
|
||||
return ModelPrivateAttr(
|
||||
default,
|
||||
default_factory=default_factory,
|
||||
)
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@ from typing import (
|
||||
from .class_validators import ValidatorGroup, extract_root_validators, extract_validators, inherit_validators
|
||||
from .error_wrappers import ErrorWrapper, ValidationError
|
||||
from .errors import ConfigError, DictError, ExtraError, MissingError
|
||||
from .fields import SHAPE_MAPPING, ModelField, PrivateAttr, Undefined
|
||||
from .fields import SHAPE_MAPPING, ModelField, ModelPrivateAttr, PrivateAttr, Undefined
|
||||
from .json import custom_pydantic_encoder, pydantic_encoder
|
||||
from .parse import Protocol, load_file, load_str_bytes
|
||||
from .schema import default_ref_template, model_schema
|
||||
@@ -215,7 +215,7 @@ class ModelMetaclass(ABCMeta):
|
||||
validators: 'ValidatorListDict' = {}
|
||||
|
||||
pre_root_validators, post_root_validators = [], []
|
||||
private_attributes: Dict[str, PrivateAttr] = {}
|
||||
private_attributes: Dict[str, ModelPrivateAttr] = {}
|
||||
slots: Set[str] = namespace.get('__slots__', ())
|
||||
slots = {slots} if isinstance(slots, str) else set(slots)
|
||||
|
||||
@@ -271,7 +271,7 @@ class ModelMetaclass(ABCMeta):
|
||||
|
||||
for var_name, value in namespace.items():
|
||||
can_be_changed = var_name not in class_vars and not isinstance(value, untouched_types)
|
||||
if isinstance(value, PrivateAttr):
|
||||
if isinstance(value, ModelPrivateAttr):
|
||||
if not is_valid_private_name(var_name):
|
||||
raise NameError(
|
||||
f'Private attributes "{var_name}" must not be a valid field name; '
|
||||
|
||||
@@ -9,7 +9,7 @@ from datetime import date, datetime
|
||||
from typing import Any, Dict, Generic, List, Optional, TypeVar
|
||||
|
||||
from pydantic import BaseModel, NoneStr, PyObject, StrictBool, root_validator, validate_arguments, validator
|
||||
from pydantic.fields import Field
|
||||
from pydantic.fields import Field, PrivateAttr
|
||||
from pydantic.generics import GenericModel
|
||||
from pydantic.typing import ForwardRef
|
||||
|
||||
@@ -145,3 +145,7 @@ class MyConf(BaseModel):
|
||||
conf = MyConf()
|
||||
var1: date = conf.str_pyobject(2020, 12, 20)
|
||||
var2: date = conf.callable_pyobject(2111, 1, 1)
|
||||
|
||||
|
||||
class MyPrivateAttr(BaseModel):
|
||||
_private_field: str = PrivateAttr()
|
||||
|
||||
@@ -158,5 +158,5 @@ def test_slots_are_ignored():
|
||||
|
||||
|
||||
def test_default_and_default_factory_used_error():
|
||||
with pytest.raises(TypeError, match='default and default_factory args can not be used together'):
|
||||
with pytest.raises(ValueError, match='cannot specify both default and default_factory'):
|
||||
PrivateAttr(default=123, default_factory=lambda: 321)
|
||||
|
||||
Reference in New Issue
Block a user