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

Fix usage of @deprecated #8294

Merged
merged 13 commits into from Jan 9, 2024
1 change: 0 additions & 1 deletion pydantic/_internal/_model_construction.py
Expand Up @@ -246,7 +246,6 @@ def _collect_bases_data(bases: tuple[type[Any], ...]) -> tuple[set[str], set[str
'The `__fields__` attribute is deprecated, use `model_fields` instead.', category=PydanticDeprecatedSince20
)
def __fields__(self) -> dict[str, FieldInfo]:
warnings.warn('The `__fields__` attribute is deprecated, use `model_fields` instead.', DeprecationWarning)
Copy link
Contributor

Choose a reason for hiding this comment

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

The reason we have this warning is actually because this is how PyCharm decides how to show the strikethrough for deprecated attributes:
image
If this line is removed, PyCharm stops showing the strikethrough on usage, even after updating typing_extensions.

If there are downsides to having both I'm happy to try to resolve it but I think showing the deprecated stuff as deprecated in PyCharm is a significant benefit, considering we are trying to advise people against using deprecated stuff anyway (so I don't mind terribly if it emits two warnings, unless there is some significant downside to that).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing I can add back the warning for properties, and as a workaround set category=None in the decorator to avoid having the warning emitted twice.

However, this is most likely an issue with PyCharm. Do you happen to know if there's an already existing issue tracking this?

The recently defined typing spec states properties should also be marked as deprecated by type checkers (see provided example).

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah it's definitely a PyCharm issue — I found this in their issue tracker https://youtrack.jetbrains.com/issue/PY-61651/Deprecation-highlighting-with-PEP-702-deprecated-decorator and added a comment on it about our usage within Pydantic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dmontagu, is this also the case for standard methods/functions, or is it only the not working for properties?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think for everything not just properties

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, then I will add back the actual warn calls, and use category=None on the decorator. I can also create an issue to remind us to remove them again once PyCharm will have implemented support for the decorator.

Copy link
Member

Choose a reason for hiding this comment

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

@Viicos,

That would be fantastic if you could create an issue as a reminder for us to remove the warn calls

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing: #8524

return self.model_fields # type: ignore


Expand Down
19 changes: 7 additions & 12 deletions pydantic/deprecated/decorator.py
@@ -1,8 +1,7 @@
import warnings
from functools import wraps
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, TypeVar, Union, overload
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, TypeVar, Union

from typing_extensions import deprecated
from typing_extensions import deprecated, overload

sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved
from .._internal import _config, _typing_extra
from ..alias_generators import to_pascal
Expand All @@ -26,26 +25,22 @@


@overload
@deprecated(
'The `validate_arguments` method is deprecated; use `validate_call` instead.', category=PydanticDeprecatedSince20
)
def validate_arguments(func: None = None, *, config: 'ConfigType' = None) -> Callable[['AnyCallableT'], 'AnyCallableT']:
...


@overload
@deprecated(
'The `validate_arguments` method is deprecated; use `validate_call` instead.', category=PydanticDeprecatedSince20
)
def validate_arguments(func: 'AnyCallableT') -> 'AnyCallableT':
...


@deprecated(
'The `validate_arguments` method is deprecated; use `validate_call` instead.',
category=PydanticDeprecatedSince20,
stacklevel=2,
)
def validate_arguments(func: Optional['AnyCallableT'] = None, *, config: 'ConfigType' = None) -> Any:
"""Decorator to validate the arguments passed to a function."""
warnings.warn(
'The `validate_arguments` method is deprecated; use `validate_call` instead.', DeprecationWarning, stacklevel=2
)

def validate(_func: 'AnyCallable') -> 'AnyCallable':
vd = ValidatedFunction(_func, config)
Expand Down
18 changes: 9 additions & 9 deletions pydantic/deprecated/json.py
@@ -1,5 +1,4 @@
import datetime
import warnings
from collections import deque
from decimal import Decimal
from enum import Enum
Expand Down Expand Up @@ -79,14 +78,15 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:


@deprecated(
'pydantic_encoder is deprecated, use pydantic_core.to_jsonable_python instead.', category=PydanticDeprecatedSince20
'`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.',
category=PydanticDeprecatedSince20,
stacklevel=2,
)
def pydantic_encoder(obj: Any) -> Any:
from dataclasses import asdict, is_dataclass

from ..main import BaseModel

warnings.warn('pydantic_encoder is deprecated, use BaseModel.model_dump instead.', DeprecationWarning, stacklevel=2)
if isinstance(obj, BaseModel):
return obj.model_dump()
elif is_dataclass(obj):
Expand All @@ -104,12 +104,13 @@ def pydantic_encoder(obj: Any) -> Any:


# TODO: Add a suggested migration path once there is a way to use custom encoders
@deprecated('custom_pydantic_encoder is deprecated.', category=PydanticDeprecatedSince20)
@deprecated(
'`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.',
category=PydanticDeprecatedSince20,
stacklevel=2,
)
def custom_pydantic_encoder(type_encoders: Dict[Any, Callable[[Type[Any]], Any]], obj: Any) -> Any:
# Check the class type and its superclasses for a matching encoder
warnings.warn(
'custom_pydantic_encoder is deprecated, use BaseModel.model_dump instead.', DeprecationWarning, stacklevel=2
)
for base in obj.__class__.__mro__[:-1]:
try:
encoder = type_encoders[base]
Expand All @@ -121,10 +122,9 @@ def custom_pydantic_encoder(type_encoders: Dict[Any, Callable[[Type[Any]], Any]]
return pydantic_encoder(obj)


@deprecated('timedelta_isoformat is deprecated.', category=PydanticDeprecatedSince20)
@deprecated('timedelta_isoformat is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
def timedelta_isoformat(td: datetime.timedelta) -> str:
"""ISO 8601 encoding for Python timedelta object."""
warnings.warn('timedelta_isoformat is deprecated.', DeprecationWarning, stacklevel=2)
minutes, seconds = divmod(td.seconds, 60)
hours, minutes = divmod(minutes, 60)
return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S'
7 changes: 2 additions & 5 deletions pydantic/deprecated/parse.py
Expand Up @@ -2,7 +2,6 @@

import json
import pickle
import warnings
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable
Expand All @@ -22,7 +21,7 @@ class Protocol(str, Enum):
pickle = 'pickle'


@deprecated('load_str_bytes is deprecated.', category=PydanticDeprecatedSince20)
@deprecated('`load_str_bytes` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
def load_str_bytes(
b: str | bytes,
*,
Expand All @@ -32,7 +31,6 @@ def load_str_bytes(
allow_pickle: bool = False,
json_loads: Callable[[str], Any] = json.loads,
) -> Any:
warnings.warn('load_str_bytes is deprecated.', DeprecationWarning, stacklevel=2)
if proto is None and content_type:
if content_type.endswith(('json', 'javascript')):
pass
Expand All @@ -56,7 +54,7 @@ def load_str_bytes(
raise TypeError(f'Unknown protocol: {proto}')


@deprecated('load_file is deprecated.', category=PydanticDeprecatedSince20)
@deprecated('`load_file` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
def load_file(
path: str | Path,
*,
Expand All @@ -66,7 +64,6 @@ def load_file(
allow_pickle: bool = False,
json_loads: Callable[[str], Any] = json.loads,
) -> Any:
warnings.warn('load_file is deprecated.', DeprecationWarning, stacklevel=2)
path = Path(path)
b = path.read_bytes()
if content_type is None:
Expand Down
23 changes: 9 additions & 14 deletions pydantic/deprecated/tools.py
Expand Up @@ -24,14 +24,11 @@


@deprecated(
'parse_obj_as is deprecated. Use pydantic.TypeAdapter.validate_python instead.', category=PydanticDeprecatedSince20
'`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.',
category=PydanticDeprecatedSince20,
stacklevel=2,
)
def parse_obj_as(type_: type[T], obj: Any, type_name: NameFactory | None = None) -> T:
warnings.warn(
'parse_obj_as is deprecated. Use pydantic.TypeAdapter.validate_python instead.',
DeprecationWarning,
stacklevel=2,
)
if type_name is not None: # pragma: no cover
warnings.warn(
'The type_name parameter is deprecated. parse_obj_as no longer creates temporary models',
Expand All @@ -42,7 +39,9 @@ def parse_obj_as(type_: type[T], obj: Any, type_name: NameFactory | None = None)


@deprecated(
'schema_of is deprecated. Use pydantic.TypeAdapter.json_schema instead.', category=PydanticDeprecatedSince20
'`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
category=PydanticDeprecatedSince20,
stacklevel=2,
)
def schema_of(
type_: Any,
Expand All @@ -53,9 +52,6 @@ def schema_of(
schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
) -> dict[str, Any]:
"""Generate a JSON schema (as dict) for the passed model or dynamically generated one."""
warnings.warn(
'schema_of is deprecated. Use pydantic.TypeAdapter.json_schema instead.', DeprecationWarning, stacklevel=2
)
res = TypeAdapter(type_).json_schema(
by_alias=by_alias,
schema_generator=schema_generator,
Expand All @@ -75,7 +71,9 @@ def schema_of(


@deprecated(
'schema_json_of is deprecated. Use pydantic.TypeAdapter.json_schema instead.', category=PydanticDeprecatedSince20
'`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
category=PydanticDeprecatedSince20,
stacklevel=2,
)
def schema_json_of(
type_: Any,
Expand All @@ -87,9 +85,6 @@ def schema_json_of(
**dumps_kwargs: Any,
) -> str:
"""Generate a JSON schema (as JSON) for the passed model or dynamically generated one."""
warnings.warn(
'schema_json_of is deprecated. Use pydantic.TypeAdapter.json_schema instead.', DeprecationWarning, stacklevel=2
)
return json.dumps(
schema_of(type_, title=title, by_alias=by_alias, ref_template=ref_template, schema_generator=schema_generator),
**dumps_kwargs,
Expand Down
55 changes: 3 additions & 52 deletions pydantic/main.py
Expand Up @@ -987,7 +987,6 @@ def __str__(self) -> str:
'The `__fields__` attribute is deprecated, use `model_fields` instead.', category=PydanticDeprecatedSince20
)
def __fields__(self) -> dict[str, FieldInfo]:
warnings.warn('The `__fields__` attribute is deprecated, use `model_fields` instead.', DeprecationWarning)
return self.model_fields

@property
Expand All @@ -996,9 +995,6 @@ def __fields__(self) -> dict[str, FieldInfo]:
category=PydanticDeprecatedSince20,
)
def __fields_set__(self) -> set[str]:
warnings.warn(
'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.', DeprecationWarning
)
return self.__pydantic_fields_set__

@typing_extensions.deprecated(
Expand All @@ -1014,7 +1010,6 @@ def dict( # noqa: D102
exclude_defaults: bool = False,
exclude_none: bool = False,
) -> typing.Dict[str, Any]: # noqa UP006
warnings.warn('The `dict` method is deprecated; use `model_dump` instead.', DeprecationWarning)
return self.model_dump(
include=include,
exclude=exclude,
Expand All @@ -1040,7 +1035,6 @@ def json( # noqa: D102
models_as_dict: bool = PydanticUndefined, # type: ignore[assignment]
**dumps_kwargs: Any,
) -> str:
warnings.warn('The `json` method is deprecated; use `model_dump_json` instead.', DeprecationWarning)
if encoder is not PydanticUndefined:
raise TypeError('The `encoder` argument is no longer supported; use field serializers instead.')
if models_as_dict is not PydanticUndefined:
Expand All @@ -1061,7 +1055,6 @@ def json( # noqa: D102
'The `parse_obj` method is deprecated; use `model_validate` instead.', category=PydanticDeprecatedSince20
)
def parse_obj(cls: type[Model], obj: Any) -> Model: # noqa: D102
warnings.warn('The `parse_obj` method is deprecated; use `model_validate` instead.', DeprecationWarning)
return cls.model_validate(obj)

@classmethod
Expand All @@ -1079,11 +1072,6 @@ def parse_raw( # noqa: D102
proto: DeprecatedParseProtocol | None = None,
allow_pickle: bool = False,
) -> Model: # pragma: no cover
warnings.warn(
'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, '
'otherwise load the data then use `model_validate` instead.',
DeprecationWarning,
)
from .deprecated import parse

try:
Expand Down Expand Up @@ -1132,11 +1120,6 @@ def parse_file( # noqa: D102
proto: DeprecatedParseProtocol | None = None,
allow_pickle: bool = False,
) -> Model:
warnings.warn(
'The `parse_file` method is deprecated; load the data from file, then if your data is JSON '
'use `model_validate_json` otherwise `model_validate` instead.',
DeprecationWarning,
)
from .deprecated import parse

obj = parse.load_file(
Expand All @@ -1155,11 +1138,6 @@ def parse_file( # noqa: D102
category=PydanticDeprecatedSince20,
)
def from_orm(cls: type[Model], obj: Any) -> Model: # noqa: D102
warnings.warn(
'The `from_orm` method is deprecated; set `model_config["from_attributes"]=True` '
'and use `model_validate` instead.',
DeprecationWarning,
)
if not cls.model_config.get('from_attributes', None):
raise PydanticUserError(
'You must set the config attribute `from_attributes=True` to use from_orm', code=None
Expand All @@ -1171,11 +1149,12 @@ def from_orm(cls: type[Model], obj: Any) -> Model: # noqa: D102
'The `construct` method is deprecated; use `model_construct` instead.', category=PydanticDeprecatedSince20
)
def construct(cls: type[Model], _fields_set: set[str] | None = None, **values: Any) -> Model: # noqa: D102
warnings.warn('The `construct` method is deprecated; use `model_construct` instead.', DeprecationWarning)
return cls.model_construct(_fields_set=_fields_set, **values)

@typing_extensions.deprecated(
'The copy method is deprecated; use `model_copy` instead.', category=PydanticDeprecatedSince20
'The `copy` method is deprecated; use `model_copy` instead. '
'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.',
category=PydanticDeprecatedSince20,
)
def copy(
self: Model,
Expand Down Expand Up @@ -1210,11 +1189,6 @@ def copy(
Returns:
A copy of the model with included, excluded and updated fields as specified.
"""
warnings.warn(
'The `copy` method is deprecated; use `model_copy` instead. '
'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.',
DeprecationWarning,
)
from .deprecated import copy_internals

values = dict(
Expand Down Expand Up @@ -1258,7 +1232,6 @@ def copy(
def schema( # noqa: D102
cls, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE
) -> typing.Dict[str, Any]: # noqa UP006
warnings.warn('The `schema` method is deprecated; use `model_json_schema` instead.', DeprecationWarning)
return cls.model_json_schema(by_alias=by_alias, ref_template=ref_template)

@classmethod
Expand All @@ -1271,10 +1244,6 @@ def schema_json( # noqa: D102
) -> str: # pragma: no cover
import json

warnings.warn(
'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.',
DeprecationWarning,
)
from .deprecated.json import pydantic_encoder

return json.dumps(
Expand All @@ -1288,7 +1257,6 @@ def schema_json( # noqa: D102
'The `validate` method is deprecated; use `model_validate` instead.', category=PydanticDeprecatedSince20
)
def validate(cls: type[Model], value: Any) -> Model: # noqa: D102
warnings.warn('The `validate` method is deprecated; use `model_validate` instead.', DeprecationWarning)
return cls.model_validate(value)

@classmethod
Expand All @@ -1297,9 +1265,6 @@ def validate(cls: type[Model], value: Any) -> Model: # noqa: D102
category=PydanticDeprecatedSince20,
)
def update_forward_refs(cls, **localns: Any) -> None: # noqa: D102
warnings.warn(
'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.', DeprecationWarning
)
if localns: # pragma: no cover
raise TypeError('`localns` arguments are not longer accepted.')
cls.model_rebuild(force=True)
Expand All @@ -1308,8 +1273,6 @@ def update_forward_refs(cls, **localns: Any) -> None: # noqa: D102
'The private method `_iter` will be removed and should no longer be used.', category=PydanticDeprecatedSince20
)
def _iter(self, *args: Any, **kwargs: Any) -> Any:
warnings.warn('The private method `_iter` will be removed and should no longer be used.', DeprecationWarning)

from .deprecated import copy_internals

return copy_internals._iter(self, *args, **kwargs)
Expand All @@ -1319,10 +1282,6 @@ def _iter(self, *args: Any, **kwargs: Any) -> Any:
category=PydanticDeprecatedSince20,
)
def _copy_and_set_values(self, *args: Any, **kwargs: Any) -> Any:
warnings.warn(
'The private method `_copy_and_set_values` will be removed and should no longer be used.',
DeprecationWarning,
)
from .deprecated import copy_internals

return copy_internals._copy_and_set_values(self, *args, **kwargs)
Expand All @@ -1333,10 +1292,6 @@ def _copy_and_set_values(self, *args: Any, **kwargs: Any) -> Any:
category=PydanticDeprecatedSince20,
)
def _get_value(cls, *args: Any, **kwargs: Any) -> Any:
warnings.warn(
'The private method `_get_value` will be removed and should no longer be used.', DeprecationWarning
)

from .deprecated import copy_internals

return copy_internals._get_value(cls, *args, **kwargs)
Expand All @@ -1346,10 +1301,6 @@ def _get_value(cls, *args: Any, **kwargs: Any) -> Any:
category=PydanticDeprecatedSince20,
)
def _calculate_keys(self, *args: Any, **kwargs: Any) -> Any:
warnings.warn(
'The private method `_calculate_keys` will be removed and should no longer be used.', DeprecationWarning
)

from .deprecated import copy_internals

return copy_internals._calculate_keys(self, *args, **kwargs)
Expand Down