Reputation: 1121
I'm reading pyinvoke docs and I'm searching any easy way to execute invoke tasks from other python script. Tasks don't have method run so I can't import them and simply .run(). I found that there is Executor Class but how I understand I need to first declare Collection of tasks and then I can run one of tasks from script. Maybe there is another way to do it easiest from other python script which isn't task?
Upvotes: 3
Views: 1119
Reputation: 10801
The one downside of the existing answer is that it tasks that are invoked by calling them do not have their "pre" or "post" tasks called as well. If you want to do that, the link that @florisla posted has an example. Here's how I've gotten it to work:
Say you have some tasks defined in some "library tasks" module somewhere outside of your tasks.py
that you want to include and call in your tasks.py
via a task named taskC
. In this example, taskB
calls taskA
as a pre task. So we want taskC
to import and call taskB
, and the "pre" definition of taskB
should call taskA
.
Here's what that "library tasks" module might look like:
# task_lib/core_tasks.py
from invoke import task
@task
def taskA(ctx):
print("A")
@task(pre=[taskA])
def taskB(ctx):
print("B")
Next, we define a module where we can place some boilerplate tasks code. That module will contain the definition for the discover_tasks
function. This is from the github issue link.
# task_lib/common.py
from invoke import Collection
from invoke import task
from invoke.executor import Executor
from invoke.main import program
def discover_tasks(tasks):
namespace = Collection(
*tasks
)
def invoke_execute(context, command_name, **kwargs):
results = Executor(namespace, config=context.config, core=program.core).execute((command_name, kwargs))
target_task = context.root_namespace[command_name]
return results[target_task]
namespace.configure({
'root_namespace': namespace,
'invoke_execute': invoke_execute,
})
return namespace
Finally, in our tasks.py
, we put everything together.
# tasks.py
from invoke import task
from task_lib.core_tasks import taskB
from task_lib.common import discover_tasks
@task
def taskC(ctx):
ctx.invoke_execute(ctx, 'taskB')
print("C")
# this function call must inform invoke about "ALL" the tasks that
# should be exposed in tasks.py (i.e. everything that will show with 'inv --list'),
# AND any imported tasks
namespace = discover_tasks(tasks=[
taskB,
taskC,
])
Now, when you invoke taskC
and it calls taskB
, then taskB
will also call its pre-task taskA
.
$ inv taskC
A
B
C
It's hacky solution, but it works.
If you DON'T use this boilerplate, and define the tasks.py
as follows...
from invoke import task
from task_lib.core_tasks import taskB
@task
def taskC(ctx):
taskB(ctx)
print("C")
... then taskB
does NOT call its dependencies.
$ inv taskC
B
C
Upvotes: 1
Reputation: 45
The point is "task is also a python function", so you can invoke task by calling task function as following:
Define a task function test
in a.py
.
#a.py
from invoke import task
@task
def test(c):
print("hello, I'm a-test!")
Import module a
and execute task by calling a.test()
function.
#tasks.py
from invoke import task
import a
@task
def executeatest(c):
print('executing [a-test] task defined in "a.py"')
a.test(c)
When you execute inv executeatest
, it results:
executing [a-test] task defined in "a.py"
hello, I'm a-test!
Upvotes: 1