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
11 changes: 5 additions & 6 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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
15 changes: 5 additions & 10 deletions pydantic/deprecated/decorator.py
@@ -1,4 +1,3 @@
import warnings
from functools import wraps
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, TypeVar, Union, overload

Expand Down Expand Up @@ -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