Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove DeprecationWarnings from v1 release & fix coverage #2415

Merged
merged 4 commits into from Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
76 changes: 0 additions & 76 deletions docs/version_1_release_notes.md

This file was deleted.

1 change: 0 additions & 1 deletion mkdocs.yml
Expand Up @@ -28,7 +28,6 @@ extra_javascript:
nav:
- Overview: index.md
- install.md
- 'Version 1 release notes': version_1_release_notes.md
- Usage:
- usage/models.md
- 'Field Types': usage/types.md
Expand Down
3 changes: 1 addition & 2 deletions pydantic/__init__.py
Expand Up @@ -6,7 +6,7 @@
from .env_settings import BaseSettings
from .error_wrappers import ValidationError
from .errors import *
from .fields import Field, PrivateAttr, Required, Schema
from .fields import Field, PrivateAttr, Required
from .main import *
from .networks import *
from .parse import Protocol
Expand Down Expand Up @@ -34,7 +34,6 @@
# fields
'Field',
'Required',
'Schema',
# main
'BaseConfig',
'BaseModel',
Expand Down
9 changes: 3 additions & 6 deletions pydantic/fields.py
@@ -1,4 +1,3 @@
import warnings
from collections import defaultdict, deque
from collections.abc import Iterable as CollectionsIterable
from typing import (
Expand Down Expand Up @@ -59,6 +58,9 @@ def __repr__(self) -> str:
def __copy__(self: T) -> T:
return self

def __reduce__(self) -> str:
return 'Undefined'

def __deepcopy__(self: T, _: Any) -> T:
return self

Expand Down Expand Up @@ -233,11 +235,6 @@ def Field(
return field_info


def Schema(default: Any, **kwargs: Any) -> Any:
warnings.warn('`Schema` is deprecated, use `Field` instead', DeprecationWarning)
return Field(default, **kwargs)


# used to be an enum but changed to int's for small performance improvement as less access overhead
SHAPE_SINGLETON = 1
SHAPE_LIST = 2
Expand Down
38 changes: 6 additions & 32 deletions pydantic/main.py
Expand Up @@ -195,21 +195,6 @@ def prepare_config(config: Type[BaseConfig], cls_name: str) -> None:
except ValueError:
raise ValueError(f'"{cls_name}": {config.extra} is not a valid value for "extra"')

if hasattr(config, 'allow_population_by_alias'):
warnings.warn(
f'{cls_name}: "allow_population_by_alias" is deprecated and replaced by "allow_population_by_field_name"',
DeprecationWarning,
)
config.allow_population_by_field_name = config.allow_population_by_alias # type: ignore

if hasattr(config, 'case_insensitive') and any('BaseSettings.Config' in c.__qualname__ for c in config.__mro__):
warnings.warn(
f'{cls_name}: "case_insensitive" is deprecated on BaseSettings config and replaced by '
f'"case_sensitive" (default False)',
DeprecationWarning,
)
config.case_sensitive = not config.case_insensitive # type: ignore


def validate_custom_root_type(fields: Dict[str, ModelField]) -> None:
if len(fields) > 1:
Expand Down Expand Up @@ -466,18 +451,18 @@ def __setattr__(self, name, value): # noqa: C901 (ignore complexity)
self.__fields_set__.add(name)

def __getstate__(self) -> 'DictAny':
private_attrs = ((k, getattr(self, k, Undefined)) for k in self.__private_attributes__)
return {
'__dict__': self.__dict__,
'__fields_set__': self.__fields_set__,
'__private_attribute_values__': {k: getattr(self, k, Undefined) for k in self.__private_attributes__},
'__private_attribute_values__': {k: v for k, v in private_attrs if v is not Undefined},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrMrRobat I think you wrote this originally, are you happy with the change.

Basically the if value is not Undefined: below was always True, I because the behaviour of Undefined when pickled has changed slightly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrMrRobat do you have any idea why the if value is not Undefined: line in copy() now has partial coverage, but used to be covered.

I'm trying to work out, but it makes no sense.

Sorry, I know it's not your problem but it's really confusing me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it could perhaps be a change in cython, but I'm not sure. Or it could even be a bug fix in codecov? 😕 🤷

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's because now only present attrs are pickled, while in the past missing attrs used to be pickled as Undefined? Just a quick assumption, I didn't dig into new code yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe, it was the same on copy().

Hopefully I fixed the coverage without breaking anything 🤞.

}

def __setstate__(self, state: 'DictAny') -> None:
object_setattr(self, '__dict__', state['__dict__'])
object_setattr(self, '__fields_set__', state['__fields_set__'])
for name, value in state.get('__private_attribute_values__', {}).items():
if value is not Undefined:
object_setattr(self, name, value)
object_setattr(self, name, value)

def _init_private_attributes(self) -> None:
for name, private_attr in self.__private_attributes__.items():
Expand Down Expand Up @@ -859,14 +844,17 @@ def _iter(
for field_key, v in self.__dict__.items():
if (allowed_keys is not None and field_key not in allowed_keys) or (exclude_none and v is None):
continue

if exclude_defaults:
model_field = self.__fields__.get(field_key)
if not getattr(model_field, 'required', True) and getattr(model_field, 'default', _missing) == v:
continue

if by_alias and field_key in self.__fields__:
dict_key = self.__fields__[field_key].alias
else:
dict_key = field_key

if to_dict or value_include or value_exclude:
v = self._get_value(
v,
Expand Down Expand Up @@ -922,20 +910,6 @@ def __eq__(self, other: Any) -> bool:
def __repr_args__(self) -> 'ReprArgs':
return self.__dict__.items() # type: ignore

@property
def fields(self) -> Dict[str, ModelField]:
warnings.warn('`fields` attribute is deprecated, use `__fields__` instead', DeprecationWarning)
return self.__fields__

def to_string(self, pretty: bool = False) -> str:
warnings.warn('`model.to_string()` method is deprecated, use `str(model)` instead', DeprecationWarning)
return str(self)

@property
def __values__(self) -> 'DictStrAny':
warnings.warn('`__values__` attribute is deprecated, use `__dict__` instead', DeprecationWarning)
return self.__dict__


_is_base_model_class_defined = True

Expand Down
16 changes: 0 additions & 16 deletions tests/test_aliases.py
Expand Up @@ -161,22 +161,6 @@ class Config:
]


def test_population_by_alias():
with pytest.warns(DeprecationWarning, match='"allow_population_by_alias" is deprecated and replaced by'):

class Model(BaseModel):
a: str

class Config:
allow_population_by_alias = True
fields = {'a': {'alias': '_a'}}

assert Model.__config__.allow_population_by_field_name is True
assert Model(a='different').a == 'different'
assert Model(a='different').dict() == {'a': 'different'}
assert Model(a='different').dict(by_alias=True) == {'_a': 'different'}


def test_alias_child_precedence():
class Parent(BaseModel):
x: int
Expand Down
21 changes: 21 additions & 0 deletions tests/test_construction.py
Expand Up @@ -4,6 +4,7 @@
import pytest

from pydantic import BaseModel, Field, PrivateAttr
from pydantic.fields import Undefined


class Model(BaseModel):
Expand Down Expand Up @@ -253,6 +254,26 @@ def test_recursive_pickle():
assert m.__foo__ == m2.__foo__


def test_pickle_undefined():
m = ModelTwo(a=24, d=Model(a='123.45'))
m2 = pickle.loads(pickle.dumps(m))
assert m2.__foo__ == {'private'}

m.__foo__ = Undefined
m3 = pickle.loads(pickle.dumps(m))
assert not hasattr(m3, '__foo__')


def test_copy_undefined():
m = ModelTwo(a=24, d=Model(a='123.45'))
m2 = m.copy()
assert m2.__foo__ == {'private'}

m.__foo__ = Undefined
m3 = m.copy()
assert not hasattr(m3, '__foo__')


def test_immutable_copy_with_allow_mutation():
class Model(BaseModel):
a: int
Expand Down
39 changes: 9 additions & 30 deletions tests/test_edge_cases.py
Expand Up @@ -19,7 +19,7 @@
validate_model,
validator,
)
from pydantic.fields import Field, Schema
from pydantic.fields import Field

try:
import cython
Expand Down Expand Up @@ -1107,16 +1107,6 @@ class TopModel(model):
assert m.nest.modified_number == 1


def test_values_attr_deprecation():
class Model(BaseModel):
foo: int
bar: str

m = Model(foo=4, bar='baz')
with pytest.warns(DeprecationWarning, match='`__values__` attribute is deprecated, use `__dict__` instead'):
assert m.__values__ == m.__dict__


def test_init_inspection():
class Foobar(BaseModel):
x: int
Expand Down Expand Up @@ -1219,25 +1209,6 @@ def check_a(cls, v):
assert Model(a=12).a == 12


def test_scheme_deprecated():

with pytest.warns(DeprecationWarning, match='`Schema` is deprecated, use `Field` instead'):

class Model(BaseModel):
foo: int = Schema(4)


def test_fields_deprecated():
class Model(BaseModel):
v: str = 'x'

with pytest.warns(DeprecationWarning, match='`fields` attribute is deprecated, use `__fields__` instead'):
assert Model().fields.keys() == {'v'}

assert Model().__fields__.keys() == {'v'}
assert Model.__fields__.keys() == {'v'}


def test_optional_field_constraints():
class MyModel(BaseModel):
my_int: Optional[int] = Field(..., ge=3)
Expand Down Expand Up @@ -1793,3 +1764,11 @@ class User(BaseModel):
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
assert module.User(id=12).dict() == {'id': 12, 'name': 'Jane Doe'}


def test_iter_coverage():
class MyModel(BaseModel):
x: int = 1
y: str = 'a'

assert list(MyModel()._iter(by_alias=True)) == [('x', 1), ('y', 'a')]
3 changes: 1 addition & 2 deletions tests/test_main.py
Expand Up @@ -62,8 +62,7 @@ def test_ultra_simple_repr():
assert dict(m) == {'a': 10.2, 'b': 10}
assert m.dict() == {'a': 10.2, 'b': 10}
assert m.json() == '{"a": 10.2, "b": 10}'
with pytest.raises(DeprecationWarning, match=r'`model.to_string\(\)` method is deprecated'):
assert m.to_string() == 'a=10.2 b=10'
assert str(m) == 'a=10.2 b=10'


def test_default_factory_field():
Expand Down
16 changes: 0 additions & 16 deletions tests/test_settings.py
Expand Up @@ -394,22 +394,6 @@ class Config:
assert exc_info.value.errors() == [{'loc': ('foo',), 'msg': 'field required', 'type': 'value_error.missing'}]


def test_case_insensitive(monkeypatch):
class Settings1(BaseSettings):
foo: str

with pytest.warns(DeprecationWarning, match='Settings2: "case_insensitive" is deprecated on BaseSettings'):

class Settings2(BaseSettings):
foo: str

class Config:
case_insensitive = False

assert Settings1.__config__.case_sensitive is False
assert Settings2.__config__.case_sensitive is True


def test_nested_dataclass(env):
@dataclasses.dataclass
class MyDataclass:
Expand Down
6 changes: 6 additions & 0 deletions tests/test_utils.py
@@ -1,5 +1,6 @@
import collections.abc
import os
import pickle
import re
import string
import sys
Expand Down Expand Up @@ -499,3 +500,8 @@ def test_all_identical():
assert (
all_identical([a, [b], b], [a, [b], b]) is False
), 'New list objects are different objects and should therefor not be identical.'


def test_undefined_pickle():
undefined2 = pickle.loads(pickle.dumps(Undefined))
assert undefined2 is Undefined