Pydantic: Unable to assign a new function to callable with default

Created on 31 Oct 2020  路  3Comments  路  Source: samuelcolvin/pydantic

Checks

  • [ x] I added a descriptive title to this issue
  • [ x] I have searched (google, github) for similar issues and couldn't find anything
  • [ x] I have read and followed the docs and still think this is a bug

Bug

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

pydantic version: 1.6.1
            pydantic compiled: True
                 install path: /home/sven/miniconda3/envs/mathub/lib/python3.7/site-packages/pydantic
               python version: 3.7.7 (default, May  7 2020, 21:25:33)  [GCC 7.3.0]
                     platform: Linux-4.15.0-122-generic-x86_64-with-debian-buster-sid
     optional deps. installed: ['typing-extensions']
from pydantic import BaseModel
from typing import Callable

class Person(BaseModel):
    firstname: str = "Joe"
    lastname: str = "Kennedy"
    fullname: str = property(lambda self: self.firstname + " " + self.lastname)


p1 = Person()
p2 = Person(firstname="Rose")
p3 = Person(firstname="John", fullname="John F Kennedy")

print(p1.fullname, p2.fullname, p3.fullname)
>>> Joe Kennedy Rose Kennedy John Kennedy

Expected the output

>>> .... John F Kennedy

Since I pass fullname="John F Kennedy" when initializing the class, but since I originally defined it as

property(lambda self: ....)

It doesn't seem to want to be overwritten.

bug

All 3 comments

Seems like the more generic issue is that a Callable with a default cannot be reassigned a different function.

from pydantic import BaseModel
from typing import Callable
from functools import partial


class Person(BaseModel):
    firstname: str = "Joe"
    lastname: str = "Kennedy"
    fullname: Callable = lambda self: self.firstname + " " + self.lastname


p1 = Person()
p2 = Person(firstname="Rose")
p3 = Person(firstname="John", fullname=lambda:"John F Kennedy")

print(p1.fullname(), p2.fullname(), p3.fullname())
>>> Joe Kennedy Rose Kennedy John Kennedy

That's not a bug.

Pydantic does nothing with instances of FunctionType, property, classmethod, staticmethod or type (all of them defined in pydantic.main.UNTOUCHED_TYPES and can be extended through Config.keep_untouched)

Since fullname never inferred as a field, and just remain normal property object, when you pass fullname key to Person, it's just ignored since by default Config.extra is set to Extra.ignore.

Moreover, that's not how property or even regular methods work in python: you can't overwrite property just on some instance of the class, as properties are defined as descriptors on the class itself.

```py
class Foo:
@property
def a(self):
return 'original'

f = Foo()
f.__dict__['a'] = property(lambda self: 'modified') # this will never work as property
print(f.a) # original (since Foo.a is a descriptor, instance attribute is ignored)
print(f.__dict__['a']) #

Hi @SvenPVoigt

As @MrMrRobat said (thanks for the answer btw!) it's not a bug
An easy workaround could be

from typing import Dict, Optional

from pydantic import BaseModel, validator


class Person(BaseModel):
    firstname: str = "Joe"
    lastname: str = "Kennedy"
    fullname: str = None

    @validator('fullname', always=True)
    def set_default(cls, v: Optional[str], values: Dict[str, str]) -> str:
        if v is None:
            return values['firstname'] + ' ' + values['lastname']
        return v


p1 = Person()
p2 = Person(firstname="Rose")
p3 = Person(firstname="John", fullname="John F Kennedy")

print(p1.fullname) # Joe Kennedy
print(p2.fullname) # Rose Kennedy
print(p3.fullname) # John F Kennedy

Hope it helps

Was this page helpful?
0 / 5 - 0 ratings