Skip to content
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

Bind delayed task creating functions to the object instance. #307

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions doit/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,15 @@ def load_tasks(namespace, command_names=(), allow_delayed=False):
task_list = []
def _process_gen():
task_list.extend(generate_tasks(name, ref(), ref.__doc__))
def _add_delayed(tname):
def _add_delayed(tname, ref):
# If ref is a bound method this updates the DelayedLoader specification
# so that when delayed.creator is executed later (control.py:469) the
# self parameter is provided. control.py:469 may execute this function
# with any additional parameters.
#
# If ref is NOT a method this this line simply re-assigns the
# same function.
delayed.creator = ref
task_list.append(Task(tname, None, loader=delayed,
doc=delayed.creator.__doc__))

Expand All @@ -147,9 +155,9 @@ def _add_delayed(tname):
_process_gen()
elif delayed.creates: # delayed with explicit task basename
for tname in delayed.creates:
_add_delayed(tname)
_add_delayed(tname, ref)
elif allow_delayed: # delayed no explicit name, cmd run
_add_delayed(name)
_add_delayed(name, ref)
else: # delayed no explicit name, cmd list (run creator)
_process_gen()

Expand Down
32 changes: 32 additions & 0 deletions tests/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,38 @@ def task_zzz3(): # pragma: no cover
assert tasks['bar'].loader is tasks['foo'].loader
assert tasks['foo'].doc == 'not loaded task doc'

def testClassCreateAfterDecorator(self):
'Check that class-defined tasks are loaded as bound methods'
class Tasks:
@create_after('yyy2')
def task_zzz3(): # pragma: no cover
pass

# create_after annotates the function
task_list = load_tasks({'task_zzz3': Tasks().task_zzz3}, allow_delayed=True)
tasks = {t.name:t for t in task_list}
task_zzz3 = tasks['zzz3']
assert isinstance(task_zzz3.loader, DelayedLoader)
assert getattr(task_zzz3.loader.creator, '__self__', None) is not None, 'Class-defined delayed task creating method is not bound'

def testClassInitialLoadDelayedTask_creates(self, dodo):
'Check that class-defined tasks support the creates argument of @create_after'
class Tasks:
@create_after('yyy2', creates=['foo', 'bar'])
def task_zzz3(): # pragma: no cover
'''not loaded task doc'''
raise Exception('Cant be executed on load phase')

# placeholder task is created with `loader` attribute
task_list = load_tasks({'task_zzz3': Tasks().task_zzz3}, allow_delayed=True)
tasks = {t.name:t for t in task_list}
assert 'zzz3' not in tasks
f_task = tasks['foo']
assert f_task.loader.task_dep == 'yyy2'
assert getattr(f_task.loader.creator, '__self__', None) is not None, 'Class-defined delayed task creating method is not bound'
assert tasks['bar'].loader is tasks['foo'].loader
assert tasks['foo'].doc == 'not loaded task doc'

def testNameInBlacklist(self):
dodo_module = {'task_cmd_name': lambda:None}
pytest.raises(InvalidDodoFile, load_tasks, dodo_module, ['cmd_name'])
Expand Down