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

Setattribute with Extra.allow is hiding method when it's defined on class. #7631

Closed
1 task done
vendito-david opened this issue Sep 26, 2023 · 3 comments · Fixed by #7683
Closed
1 task done

Setattribute with Extra.allow is hiding method when it's defined on class. #7631

vendito-david opened this issue Sep 26, 2023 · 3 comments · Fixed by #7683
Assignees
Labels
bug V2 Bug related to Pydantic V2

Comments

@vendito-david
Copy link

vendito-david commented Sep 26, 2023

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

When setting an attribute, it is saving it with __setattr__ into __pydantic_extra__ (

self.__pydantic_extra__[name] = value # type: ignore
).
On the other hand, when getting an attribute, it is running the __getattribute__ implementation, which is basically looking for the object dict, then the class hierarchy then the __getattr__ implementation afterwards only.
If Extra.allow is set, the setattr is saving it to a place which is not accessed in the same order by default. See the example code. It prints the "1,1,patched" instead of "1,patched,patched".

Example Code

from pydantic import BaseModel, Extra

class LookupPython():
    id: int

    def __init__(self, id) -> None:
        self.id = id

    def func(self):
        return self.id

class LookupPydantic(BaseModel, extra=Extra.allow):
    id: int

    def func(self):
        return self.id
    
lookups = [ LookupPython(id=1), LookupPydantic(id=1) ]

for lookup in lookups:
    print( type(lookup) )
    print( repr(lookup) )
    print( lookup.func() )
    lookup.func = lambda: "patched"
    print( repr(lookup) )
    print( lookup.func() )
    lookup.__dict__["func"] = lambda: "patched"
    print( repr(lookup) )
    print( lookup.func() )
    print( "" )

Output:

<class '__main__.LookupPython'>
<__main__.LookupPython object at 0x7ff7c92ba5d0>
1
<__main__.LookupPython object at 0x7ff7c92ba5d0>
patched
<__main__.LookupPython object at 0x7ff7c92ba5d0>
patched

<class '__main__.LookupPydantic'>
LookupPydantic(id=1)
1
LookupPydantic(id=1, func=<function <lambda> at 0x7ff7c8e54cc0>)
1
LookupPydantic(id=1, func=<function <lambda> at 0x7ff7c8e54cc0>)
patched

Python, Pydantic & OS Version

pydantic version: 2.4.0
        pydantic-core version: 2.10.0
          pydantic-core build: profile=release pgo=true
                 install path: /home/kali/.local/lib/python3.10/site-packages/pydantic
               python version: 3.10.8 (main, Oct 24 2022, 10:07:16) [GCC 12.2.0]
                     platform: Linux-5.15.90.1-microsoft-standard-WSL2-x86_64-with-glibc2.35
             related packages: typing_extensions-4.8.0 fastapi-0.85.0 typing_extensions-4.3.0 email-validator-1.1.2
@vendito-david vendito-david added bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable labels Sep 26, 2023
@sydney-runkle sydney-runkle self-assigned this Sep 26, 2023
@sydney-runkle
Copy link
Member

Hi @vendito-david,

Thanks for reporting this! Also, thanks for the in depth code example that runs out of the box 😄.

Like you mentioned, this is constrained to just the case where you're setting a method, as the following works with attributes:

from pydantic import BaseModel

class LookupPython():
    id: int

    def __init__(self, id) -> None:
        self.id = id

class LookupPydantic(BaseModel, extra='allow'):
    id: int

python_lookup = LookupPython(id=1)
pydantic_lookup = LookupPydantic(id=1)

python_lookup.id = 2
print(python_lookup.id)
#> 2

pydantic_lookup.id = 2
print(pydantic_lookup.id)
#> 2

Are you interested in opening a PR to help fix this issue? ⭐

@sydney-runkle sydney-runkle removed the unconfirmed Bug not yet confirmed as valid/applicable label Sep 26, 2023
@mullerdavid
Copy link

mullerdavid commented Sep 26, 2023

Hi, this is vendito-david on my non-corporate user. I made an issue instead of PR because i am not that familiar with the new pydantic v2 and your conventions yet.

@sydney-runkle
Copy link
Member

Hi @vendito-david (@mullerdavid ),

No worries at all. I'll work on a fix for this + update here with progress 😄.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V2 Bug related to Pydantic V2
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants