Skip to content

Conversation

@Viicos
Copy link
Member

@Viicos Viicos commented Aug 16, 2024

Change Summary

Related issue number

Checklist

  • The pull request title is a good summary of the changes - it will be used in the changelog
  • Unit tests for the changes exist
  • Tests pass on CI
  • Documentation reflects the changes where applicable
  • My PR is ready to review, please add a comment including the phrase "please review" to assign reviewers

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Aug 16, 2024

Deploying pydantic-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 298c4d2
Status: ✅  Deploy successful!
Preview URL: https://b3b63e10.pydantic-docs.pages.dev
Branch Preview URL: https://10153.pydantic-docs.pages.dev

View logs

Copy link
Contributor

@sydney-runkle sydney-runkle left a comment

Choose a reason for hiding this comment

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

Quick question about namespaces.

Comment on lines 428 to 446
if isinstance(ann_type, str):
# Walking up the frames to get the module namespace where the model is defined
# (as the model class wasn't created yet, we unfortunately can't use `cls.__module__`):
module = inspect.getmodule(sys._getframe(2))
if module is not None:
ann_type = eval_type_backport(ForwardRef(ann_type), globalns=module.__dict__)
Copy link
Contributor

Choose a reason for hiding this comment

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

What if the model is defined in a function?

Copy link
Member Author

Choose a reason for hiding this comment

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

It will fail indeed. Not having access to the current model class makes things annoying as stated in my comment. Not sure if we can address this issue? As I said in the original issue, the Annotated pattern isn't recommended anyway.

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 we should consider many of the questions we've been thinking about for our other recent namespace management changes - how are things different if we rebuild or import a model in a new file?

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we be filtering the namespace with our new logic in _typing_extra.py?

Copy link
Member Author

Choose a reason for hiding this comment

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

how are things different if we rebuild or import a model in a new file?

In these cases we have access to the class' module, so it helps as we can easily combine the module namespace and parent namespace (if necessary).

I'll note that because the way inspect.getmodule works, the following is ok:

from typing import Annotated

from pydantic import BaseModel, PrivateAttr

def func():
    class Model(BaseModel):
        _foo: 'Annotated[int, PrivateAttr()]'

func()

(however, it does not if PrivateAttr and/or Annotated is imported inside func).


We can consider many scenarios where even our current annotation evaluation for normal fields (i.e. after cls is created in the metaclass, where all the logic to get the parent ns and merge it with the module ns is done) does not work as expected:

from __future__ import annotations

from pydantic import BaseModel


def test():
    from typing import Annotated

    def inner():

        from pydantic import Field

        class Model(BaseModel):
            a: Annotated[int, Field(default=1)]

        return Model

    Model = inner()

test()

Trying to support these cases would probably complicate things (and also maybe increase memory/cpu usage). I'm also wondering how much of these use cases can be covered by PEP 649.

@pydantic-hooky pydantic-hooky bot added the awaiting author revision awaiting changes from the PR author label Aug 16, 2024
@codspeed-hq
Copy link

codspeed-hq bot commented Aug 16, 2024

CodSpeed Performance Report

Merging #10157 will not alter performance

Comparing 10153 (298c4d2) with main (7212484)

Summary

✅ 17 untouched benchmarks

@github-actions
Copy link
Contributor

github-actions bot commented Aug 16, 2024

Coverage report

Image Image

This PR does not seem to contain any modification to coverable code.

Comment on lines 444 to 446
module = inspect.getmodule(sys._getframe(2))
if module is not None:
ann_type = eval_type_backport(ForwardRef(ann_type), globalns=module.__dict__)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
module = inspect.getmodule(sys._getframe(2))
if module is not None:
ann_type = eval_type_backport(ForwardRef(ann_type), globalns=module.__dict__)
frame = sys._getframe(2)
if frame is not None:
ann_type = eval_type_backport(ForwardRef(ann_type), globalns=frame.f_globals, localns=frame.f_locals)

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed with a movement towards this approach, but should we just use

def parent_frame_namespace(*, parent_depth: int = 2) -> dict[str, Any] | None:
?

If we go with that, then you can ignore all of my above comments, bc I think this would address them.

Copy link
Member Author

Choose a reason for hiding this comment

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

parent_frame_namespace only considers locals so I went with the frame locals and globals

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good to me. Thanks for following up!

Copy link
Contributor

@sydney-runkle sydney-runkle left a comment

Choose a reason for hiding this comment

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

Nice work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting author revision awaiting changes from the PR author relnotes-fix Used for bugfixes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants