From 0affaacbab668d6e68be6e64098902c8d5afa0ca Mon Sep 17 00:00:00 2001 From: Michael Adkins Date: Tue, 1 Jun 2021 18:25:42 -0500 Subject: [PATCH 1/3] Simplify `FunctionTask` wrapper handling with `functools.update_wrapper` --- src/prefect/tasks/core/function.py | 38 +++++++----------------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/src/prefect/tasks/core/function.py b/src/prefect/tasks/core/function.py index eb8bcc36cfe1..35b92363acca 100644 --- a/src/prefect/tasks/core/function.py +++ b/src/prefect/tasks/core/function.py @@ -6,32 +6,19 @@ """ from typing import Any, Callable +from functools import update_wrapper import prefect -class _DocProxy(object): - """A descriptor that proxies through the docstring for the wrapped task as - the docstring for a `FunctionTask` instance.""" - - def __init__(self, cls_doc): - self._cls_doc = cls_doc - - def __get__(self, obj, cls): - if obj is None: - return self._cls_doc - else: - return getattr(obj.run, "__doc__", None) or self._cls_doc - - class FunctionTask(prefect.Task): - __doc__ = _DocProxy( - """A convenience Task for functionally creating Task instances with + """A convenience Task for functionally creating Task instances with arbitrary callable `run` methods. Args: - fn (callable): the function to be the task's `run` method - - name (str, optional): the name of this task + - name (str, optional): the name of this task; if not provided it is inferred + as the function name - **kwargs: keyword arguments that will be passed to the Task constructor @@ -47,26 +34,17 @@ class FunctionTask(prefect.Task): result = task(42) ``` """ - ) def __init__(self, fn: Callable, name: str = None, **kwargs: Any): if not callable(fn): - raise TypeError("fn must be callable.") + raise TypeError("`fn` must be callable") - # set the name from the fn + # Set the Prefect name from the function if name is None: name = getattr(fn, "__name__", type(self).__name__) - prefect.core.task._validate_run_signature(fn) # type: ignore + prefect.core.task._validate_run_signature(fn) self.run = fn + update_wrapper(self, fn) super().__init__(name=name, **kwargs) - - def __getattr__(self, k): - if k == "__wrapped__": - return self.run - raise AttributeError( - f"'FunctionTask' object has no attribute {k}." - " Did you call this object within a function that should have been" - "decorated with @prefect.task?" - ) From 047c80b419cf0ee5c975d9e67cfe27734bd15c2e Mon Sep 17 00:00:00 2001 From: Michael Adkins Date: Tue, 1 Jun 2021 18:38:59 -0500 Subject: [PATCH 2/3] Update tests to reflect update_wrapper behavior --- tests/tasks/core/test_core.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/tasks/core/test_core.py b/tests/tasks/core/test_core.py index 1b0227f322a7..099733d7a15b 100644 --- a/tests/tasks/core/test_core.py +++ b/tests/tasks/core/test_core.py @@ -58,9 +58,9 @@ def my_fn(): f = FunctionTask(fn=my_fn) assert f.__doc__ == my_fn.__doc__ - # Except when no docstring on wrapped function + # Lambdas do not have a function docstring f = FunctionTask(fn=lambda x: x + 1) - assert "FunctionTask" in f.__doc__ + assert f.__doc__ is None def test_function_task_sets__wrapped__(self): def my_fn(): @@ -77,12 +77,12 @@ def my_fn(): pass t = FunctionTask(fn=my_fn) - with pytest.raises(AttributeError) as exc: + with pytest.raises( + AttributeError, + match="'FunctionTask' object has no attribute 'unknown_attribute'", + ): t.unknown_attribute - assert "unknown_attribute" in str(exc.value) - assert "@prefect.task" in str(exc.value) - class TestCollections: def test_list_returns_a_list(self): From 384011a8bfa772e1c15daec95619bfbb05177982 Mon Sep 17 00:00:00 2001 From: Michael Adkins Date: Tue, 8 Jun 2021 22:11:23 -0500 Subject: [PATCH 3/3] Add changes/pr4608.yaml --- changes/pr4608.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changes/pr4608.yaml diff --git a/changes/pr4608.yaml b/changes/pr4608.yaml new file mode 100644 index 000000000000..dcd7037db66d --- /dev/null +++ b/changes/pr4608.yaml @@ -0,0 +1,3 @@ + +enhancement: + - "Use `functools.update_wrapper` for `FunctionTask` - [#4608](https://github.com/PrefectHQ/prefect/pull/4608)"