Skip to content

Commit 4cf7876

Browse files
alejandro antillonalejandro antillon
authored andcommitted
Draft Idea For Pre Tasks To Be Context Aware
This commit introduces, in a draft state, the idea to allow tasks that are called as a pre task for another task, to keep state of this pre task assignment via the attribute `_is_pre_of`. This attribute is later used when a `Call` object is created from a `Task` instance that has said attribute set. A `Call` created from a pre task, will add a reference to the task that defines the current task as pre task, and said reference will be available during the pre task's execution in the context passed to the task. The approach here suggested uses Pythonic patterns to avoid changes to the class implementation as much as possible, but rather compose the new functionality from invidivual, signle purpose small components, such as a generic decorator, and a descriptor component. It can be very well the case that making a task more stateful by tracking this pre assignment is not aligned with the design if this library, in which case it would be better not to add functionality that is not part of the long term view for the library. I would be happy to contribute to this greaat library in other ways.
1 parent 65dd896 commit 4cf7876

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

invoke/tasks.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@
3232

3333
T = TypeVar("T", bound=Callable)
3434

35+
class PreTaskDesc:
36+
def __get__(self, obj, _type):
37+
return obj._pre
38+
39+
def __set__(self, obj, value):
40+
for p in value:
41+
p._is_pre_of = obj
42+
obj._pre = value
3543

3644
class Task(Generic[T]):
3745
"""
@@ -48,6 +56,7 @@ class Task(Generic[T]):
4856
4957
.. versionadded:: 1.0
5058
"""
59+
pre = PreTaskDesc()
5160

5261
# TODO: store these kwarg defaults central, refer to those values both here
5362
# and in @task.
@@ -395,6 +404,11 @@ def __init__(
395404
Keyword arguments to call with, if any. Default: ``None``.
396405
"""
397406
self.task = task
407+
if hasattr(task, "_is_pre_of"):
408+
self.make_context = _copy_attrs_to_return_val(
409+
task,
410+
"_is_pre_of"
411+
)(self.make_context)
398412
self.called_as = called_as
399413
self.args = args or tuple()
400414
self.kwargs = kwargs or dict()
@@ -517,3 +531,18 @@ def clean_build(c):
517531
.. versionadded:: 1.0
518532
"""
519533
return Call(task, args=args, kwargs=kwargs)
534+
535+
def _copy_attrs_to_return_val(source, *attrs):
536+
"""
537+
Copy attributes from a source to the return value of the decorated func
538+
"""
539+
def _wrapper(func):
540+
def _inner(*args, **kwargs):
541+
target = func(*args, **kwargs)
542+
for name in attrs:
543+
value = getattr(source, name)
544+
if value:
545+
setattr(target, name, value)
546+
return target
547+
return _inner
548+
return _wrapper

tests/task.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,33 @@ def func(c):
9292

9393
assert func.pre == [whatever]
9494

95+
def task_and_pre_tasks_binding(self):
96+
97+
@task
98+
def pre_task(c):
99+
pass
100+
101+
@task(pre=[pre_task])
102+
def my_task(c):
103+
pass
104+
105+
assert all([hasattr(p, "_is_pre_of") for p in my_task.pre])
106+
assert pre_task._is_pre_of == my_task
107+
108+
def create_call_and_context_form_pre_task_has_access_to_parent_task(self):
109+
110+
@task
111+
def pre_task(c):
112+
pass
113+
114+
@task(pre=[pre_task])
115+
def my_task(c):
116+
pass
117+
118+
call = Call(pre_task)
119+
c = call.make_context(Config(defaults={}))
120+
assert getattr(c, "_is_pre_of") == my_task
121+
95122
def allows_star_args_as_shortcut_for_pre(self):
96123
@task
97124
def pre1(c):

0 commit comments

Comments
 (0)