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
Callables are attached as bound methods when used as default args #1596
Comments
Thanks for reporting, not sure how hard this would be to fix. In the meantime you can probably use |
It's not a show stopper by any stretch. I really appreciate the library, and have enjoyed using it! |
@samuelcolvin I ran into the same bug and I did try what you've suggested, except with the dataclasses. The from typing import Callable
from dataclasses import field as dc_field, dataclass as dc_dataclass
from pydantic import dataclasses, BaseModel, Field
import pytest
def foo(arg1, arg2):
return arg1, arg2
class WorkaroundCallable(Callable):
@classmethod
def validate_callable(cls, v):
if not callable(v):
raise ValueError("invalid callable passed")
return v
@classmethod
def __get_validators__(cls):
yield cls.validate_callable
@dataclasses.dataclass()
class HasCallables:
non_default_callable: Callable
default_callable: Callable = lambda x: foo(x, "default")
default_callable_factory: Callable = dc_field(default=lambda x: foo(x, "factory"))
@dataclasses.dataclass()
class HasCallablesStatic:
non_default_callable: Callable
default_callable: Callable = staticmethod(lambda x: foo(x, "default"))
default_callable_factory: Callable = dc_field(default=staticmethod(lambda x: foo(x, "factory")))
@dataclasses.dataclass()
class HasCallablesWorkaround:
non_default_callable: WorkaroundCallable
default_callable: WorkaroundCallable = lambda x: foo(x, "default")
default_callable_factory: WorkaroundCallable = dc_field(default_factory=lambda: lambda x: foo(x, "factory"))
class HasCallablesModel(BaseModel):
non_default_callable: Callable
default_callable: Callable = lambda x: foo(x, "default")
default_callable_factory: Callable = Field(default_factory=lambda: lambda x: foo(x, "factory"))
@dc_dataclass()
class HasCallablesDC:
non_default_callable: Callable
default_callable: Callable = lambda x: foo(x, "default")
default_callable_factory: Callable = dc_field(default_factory=lambda: lambda x: foo(x, "factory"))
@pytest.mark.parametrize("cls", [HasCallables, HasCallablesModel, HasCallablesStatic])
def test_pydantic_callable_static(cls):
"""tests pydantic callable behavior"""
non_default_callable = lambda x: foo(x, "nondefault")
a1 = cls(non_default_callable=non_default_callable)
a2 = HasCallablesDC(non_default_callable=non_default_callable)
# call non_default
assert a1.non_default_callable("hello") == a2.non_default_callable("hello")
# call default_factory
assert a1.default_callable_factory("hello") == a2.default_callable_factory("hello")
# call default
assert a1.default_callable("hello") == a2.default_callable("hello") |
FWIW: This isn't a problem in the built-in |
@antonl @juanarrivillaga Thanks for reporting! I missed this issue... |
@PrettyWood thanks! This looks great! |
Bug
Methods used as default args to Callable are attached as bound methods of the BaseModel class. Things work fine if you pass the method as an argument to the constructor. One workaround (if you must have a default arg) is to pass the method as a
functools.partial
object.Output of
python -c "import pydantic.utils; print(pydantic.utils.version_info())"
:Test
Output
The text was updated successfully, but these errors were encountered: