-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Add support for instance/class/static methods #13944
Conversation
fyi: I was curious about In [1]: from pydantic import BaseModel
In [2]: from prefect import flow
In [3]: class Foo(BaseModel):
...: x: int
...: @flow
...: def f(self):
...: print(self.x)
PydanticUserError: A non-annotated attribute was detected: `f = <prefect.flows.Flow object at 0x106e84950>`. All model fields require a type annotation; if `f` is not meant to be a field, you may be able to resolve this error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`. here's how it can work def test_flow_supports_instance_methods_with_basemodel(self):
class Foo(pydantic.BaseModel):
model_config = pydantic.ConfigDict(ignored_types=(Flow,))
x: int = 5
@flow
def instance_method(self):
return self.x
assert Foo().instance_method() == 5
assert isinstance(Foo().instance_method, Flow) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clever - can you add some tests to confirm that self
is properly provided to task input cache policies? (or really any hook that engages with task inputs, could also be result_storage_key
)
@cicdw's request demonstrated that the original approach did not satisfy Prefect's internal parameter machinery since it was too implicit. I've updated the PR to reflect a new, far simpler, approach. Instead of returning a bound instance from the descriptor method, we store the bound instance on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
Very cool! |
This PR adds support for decorating instance/class/static methods as @flows or @tasks.
Today, methods aren't supported because of how Prefect handles the implicit
self
argument; you'll get an error likeParameterBindError: Error binding parameters for function 'instance_method': missing a required argument: 'self'. Function 'instance_method' has signature 'self' but received args: () and kwargs: [].
.This is because the function signature (used for validating parameters) demands an explicit
self
call arg that will never be supplied by the user.The solution (for both flows and tasks) has two parts:
Flows
andTasks
by adding a__get__
method. When an instance method is accessed on its parent instance, the__get__
method is called with the instance as its first argument. It is expected to return the bound method. We use that arg to create and return a copy of the Flow object which has that instance bound to its ownfn
as a__prefect_self__
attribute.get_call_parameters
function (which transforms user-supplied arguments into a full set of callargs) to look for__prefect_self__
and, if found, prepend it to the front of the callargs.This solution means: