Skip to content

Conversation

Viicos
Copy link
Member

@Viicos Viicos commented Jun 30, 2025

Best reviewed commit per commit.

In a nutshell, this now raises a warning:

from typing import Annotated

from pydantic import BaseModel, Field

type MyInt = Annotated[int, Field(alias='my_alias')]

class Model(BaseModel):
    # see the warning in https://docs.pydantic.dev/latest/concepts/types/#named-type-aliases for more info:
    a: MyInt
"""
>>> UnsupportedFieldAttributeWarning: The 'alias' attribute with value 'my_alias' was provided to the `Field()` function, which 
>>> is unsupported in the context it was used. 'alias' is field-specific metadata, and can only be attached to a model field 
>>> using `Annotated` metadata or by assignment (note that using `Annotated` type aliases using the `type` statement 
>>> isn't supported).
"""

# Other use case:
class Model(BaseModel):
    a: list[Annotated[int, Field(default=1)]]
"""
>>> UnsupportedFieldAttributeWarning: The 'default' attribute with value 1 was provided to the `Field()` function, which 
>>> is unsupported in the context it was used. 'alias' is field-specific metadata, and can only be attached to a model field 
>>> using `Annotated` metadata or by assignment (note that using `Annotated` type aliases using the `type` statement 
>>> isn't supported).
"""

We still need to figure out if we want the warning to be raised for default and default_factory. Currently, the following works:

type MyInt = Annotated[int, Field(default=1)]

class Model(BaseModel):
    a: MyInt

Model()
#> Model(a=1)

This is explained by the fact that we need to support defaults in type adapters:

TypeAdapter(Annotated[int, Field(default=1)]).get_default_vaue()
#> Some(1)

And as a consequence it accidentally worked for models as well. However, things are inconsistent:

type MyInt = Annotated[int, Field(default=1)]

class Model(BaseModel):
    a: MyInt

Model.model_fields['a'].is_required()  # True

and JSON Schema generation is also acting weirdly: #12024.

Imo, I think we should keep the warning for default and default_factory. Sure, validation behavior still works, but it is a footgun for the reasons mentioned above. Users can still ignore the warning if they feel like it.


I've reached out to the FastAPI team regarding the failing tests. They will need to address them in some way.

@github-actions github-actions bot added the relnotes-fix Used for bugfixes. label Jun 30, 2025
Comment on lines -416 to -429
def _warn_on_nested_alias_in_annotation(ann_type: type[Any], ann_name: str) -> None:
FieldInfo = import_cached_field_info()

args = getattr(ann_type, '__args__', None)
if args:
for anno_arg in args:
if typing_objects.is_annotated(get_origin(anno_arg)):
for anno_type_arg in _typing_extra.get_args(anno_arg):
if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None:
warnings.warn(
f'`alias` specification on field "{ann_name}" must be set on outermost annotation to take effect.',
UserWarning,
)
return
Copy link
Member Author

Choose a reason for hiding this comment

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

This was fragile logic implemented a long time ago, that is now fully covered by the new warning.

Comment on lines 582 to 587
with warnings.catch_warnings():
# We kind of abused `Field()` default factories to be able to specify
# the `defaultdict`'s `default_factory`. As a consequence, we get warnings
# as normally `FieldInfo.default_factory` is unsupported in the context where
# `Field()` is used and our only solution is to ignore them (note that this might
# wrongfully ignore valid warnings, e.g. if the `value_type` to a PEP 695 type alias
Copy link
Member Author

Choose a reason for hiding this comment

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

This is referring to the following use case:

class Model(BaseModel):
    a: defaultdict[int, Annotated[list[int], Field(default_factory=lambda: MyList())]]

Comment on lines +1556 to +1559
# Because we pass `field` as metadata above (required for attributes relevant for
# JSON Scheme generation), we need to ignore the potential warnings about `FieldInfo`
# attributes that will not be used:
check_unsupported_field_info_attributes=False,
Copy link
Member Author

Choose a reason for hiding this comment

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

This is for validated function calls (see test_unsupported_field_attribute_nested_with_function()). We don't want to raise a warning for:

@validate_call
def func(a: Annotated[int, Field(alias='b')]): ...

@Viicos Viicos requested review from DouweM and davidhewitt June 30, 2025 17:40
@Viicos Viicos added relnotes-change Used for changes to existing functionality which don't have a better categorization. needs-blogpost-entry This PR needs to be documented in the release notes blog post and removed relnotes-fix Used for bugfixes. labels Jun 30, 2025
Copy link
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  pydantic
  functional_validators.py
  warnings.py
  pydantic/_internal
  _fields.py
  _generate_schema.py
Project Total  

This report was generated by python-coverage-comment-action

Copy link

codspeed-hq bot commented Jun 30, 2025

CodSpeed Performance Report

Merging #12028 will not alter performance

Comparing field-info-attr-warning (705951a) with main (dac3c43)

Summary

✅ 46 untouched benchmarks

@Viicos Viicos added the third-party-tests Add this label on a PR to trigger 3rd party tests label Jun 30, 2025
@Viicos Viicos closed this Jun 30, 2025
@Viicos Viicos reopened this Jun 30, 2025
Copy link

cloudflare-workers-and-pages bot commented Jun 30, 2025

Deploying pydantic-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 705951a
Status: ✅  Deploy successful!
Preview URL: https://e6f68bfe.pydantic-docs.pages.dev
Branch Preview URL: https://field-info-attr-warning.pydantic-docs.pages.dev

View logs

@Viicos Viicos force-pushed the field-info-attr-warning branch from 59be012 to 6c868fa Compare June 30, 2025 20:04
@Viicos Viicos force-pushed the field-info-attr-warning branch from 91fc505 to 30acc55 Compare July 1, 2025 15:04
@Viicos
Copy link
Member Author

Viicos commented Jul 1, 2025

I'm wondering if, similar to usage errors, we should link to a new documentation section about warnings, to show examples of this issue.

@DouweM
Copy link
Contributor

DouweM commented Jul 1, 2025

@Viicos If we haven't documented yet that this is unsupported, I agree we should

@Viicos
Copy link
Member Author

Viicos commented Jul 1, 2025

Well it is documented, but I was thinking about linking to the docs from the warning message, so that users don't get confused when they see it.

@DouweM
Copy link
Contributor

DouweM commented Jul 1, 2025

@Viicos Ah I like that!

@Viicos
Copy link
Member Author

Viicos commented Jul 5, 2025

@Viicos Ah I like that!

Actually this is going to be a bit hard to implement as is, as we'll need to set up the same logic as for errors (that is, construct the URL from the current version, etc). I improved the warning message and we can revisit if users still get confused.

@Viicos Viicos force-pushed the field-info-attr-warning branch from b87d649 to 705951a Compare July 5, 2025 10:21
@Viicos Viicos merged commit 3a7fe26 into main Jul 18, 2025
85 of 90 checks passed
@Viicos Viicos deleted the field-info-attr-warning branch July 18, 2025 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-blogpost-entry This PR needs to be documented in the release notes blog post relnotes-change Used for changes to existing functionality which don't have a better categorization. third-party-tests Add this label on a PR to trigger 3rd party tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants