Skip to content

Commit

Permalink
Add print_result option for script tasks (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
nat-n committed Jul 17, 2022
1 parent 90107d1 commit 494acd7
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 31 deletions.
40 changes: 40 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,46 @@ scripts *(shell)*, and sequence tasks *(sequence)*.
declared), then they will be available within the called python function via
:python:`sys.argv`.

**Calling standard library functions**

Any python callable accessible via the python path can be referenced, including the
standard library. This can be useful for ensuring that tasks work across platforms.

For example, the following task will not always work on windows:

.. code-block:: toml
[[tool.poe.tasks.build]]
cmd = "mkdir -p build/assets"
whereas the same behaviour can can be reliably achieved like so:

.. code-block:: toml
[[tool.poe.tasks.build]]
script = "os:makedirs('build/assets', exist_ok=True)"
**Output the return value from the python callable**

Script tasks can be configured to output the return value of a callable using the
:toml:`print_result` option like so:

.. code-block:: toml
[tool.poe.tasks.create-secret]
script = "django.core.management.utils:get_random_secret_key()"
print_result = true
Given the above configuration running the following command would output just the
generated key.

.. code-block:: bash
poe -q create-secret
Note that if the return value is None then the :toml:`print_result` option has no
effect.

- **Shell tasks** are similar to simple command tasks except that they are executed
inside a new shell, and can consist of multiple separate commands, command
substitution, pipes, background processes, etc.
Expand Down
27 changes: 16 additions & 11 deletions poethepoet/task/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ScriptTask(PoeTask):
__key__ = "script"
__options__: Dict[str, Union[Type, Tuple[Type, ...]]] = {
"use_exec": bool,
"print_result": bool,
}

def _handle_run(
Expand All @@ -48,17 +49,21 @@ def _handle_run(
self.name,
*(env.fill_template(token) for token in extra_args),
]
cmd = (
# Exactly which python executable to use is usually resolved by the executor
"python",
"-c",
"import sys; "
"from os import environ; "
"from importlib import import_module; "
f"sys.argv = {argv!r}; sys.path.append('src');"
f"{self.format_args_class(self.named_args)}"
f"import_module('{target_module}').{function_call}",
)

script = [
"import sys; ",
"from os import environ; ",
"from importlib import import_module; ",
f"sys.argv = {argv!r}; sys.path.append('src');",
f"{self.format_args_class(self.named_args)}",
f"result = import_module('{target_module}').{function_call};",
]

if self.options.get("print_result"):
script.append(f"result is not None and print(result);")

# Exactly which python executable to use is usually resolved by the executor
cmd = ("python", "-c", "".join(script))

self._print_action(" ".join(argv), context.dry)
return context.get_executor(self.invocation, env, self.options).execute(
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/scripts_project/pkg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ def echo_args():
print("hello", *sys.argv[1:])


def get_random_number():
print("determining most random number...")
return 7


def echo_script(*args, **kwargs):
print("args", args)
print("kwargs", kwargs)
Expand Down
9 changes: 8 additions & 1 deletion tests/fixtures/scripts_project/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ call_attrs.use_exec = true

greet = "pkg:greet"


[tool.poe.tasks.print-script-result]
script = "pkg:get_random_number"
print_result = true

[tool.poe.tasks.dont-print-script-result]
script = "pkg:get_random_number"

[tool.poe.tasks.greet-passed-args]
# weird formatting is intentional
script = """
Expand Down Expand Up @@ -100,7 +108,6 @@ greet = "pkg:greet"
options = ["--upper"]
type = "boolean"


[tool.poe.tasks.multiple-value-args]
script = "pkg:echo_script(first, second, widgets=widgets, engines=engines)"

Expand Down
40 changes: 21 additions & 19 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,26 @@ def test_documentation_of_task_named_args(run_poe):
), "Output should include status message"
assert re.search(
r"CONFIGURED TASKS\n"
r" composite_task \s+\n"
r" echo-args \s+\n"
r" static-args-test \s+\n"
r" call_attrs \s+\n"
r" greet \s+\n"
r" greet-passed-args\s+\n"
r" --greeting \s+\n"
r" --user \s+\n"
r" --optional \s+\n"
r" --upper \s+\n"
r" greet-full-args \s+\n"
r" --greeting, -g \s+\n"
r" --user \s+\n"
r" --upper \s+\n"
r" --age, -a \s+\n"
r" --height, -h \s+The user's height in meters\n"
r" greet-strict \s+All arguments are required\n"
r" --greeting \s+this one is required\n"
r" --name \s+and this one is required\n",
r" composite_task \s+\n"
r" echo-args \s+\n"
r" static-args-test \s+\n"
r" call_attrs \s+\n"
r" greet \s+\n"
r" print-script-result \s+\n"
r" dont-print-script-result \s+\n"
r" greet-passed-args \s+\n"
r" --greeting \s+\n"
r" --user \s+\n"
r" --optional \s+\n"
r" --upper \s+\n"
r" greet-full-args \s+\n"
r" --greeting, -g \s+\n"
r" --user \s+\n"
r" --upper \s+\n"
r" --age, -a \s+\n"
r" --height, -h \s+The user's height in meters\n"
r" greet-strict \s+All arguments are required\n"
r" --greeting \s+this one is required\n"
r" --name \s+and this one is required\n",
result.capture,
)
14 changes: 14 additions & 0 deletions tests/test_script_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ def test_call_attr_func_with_exec(run_poe_subproc):
assert result.stderr == ""


def test_print_result(run_poe_subproc):
result = run_poe_subproc("print-script-result", project="scripts")
assert result.capture == "Poe => print-script-result\n"
assert result.stdout == "determining most random number...\n7\n"
assert result.stderr == ""


def test_dont_print_result(run_poe_subproc):
result = run_poe_subproc("dont-print-script-result", project="scripts")
assert result.capture == "Poe => dont-print-script-result\n"
assert result.stdout == "determining most random number...\n"
assert result.stderr == ""


def test_automatic_kwargs_from_args(run_poe_subproc):
result = run_poe_subproc("greet", project="scripts")
assert result.capture == "Poe => greet\n"
Expand Down

0 comments on commit 494acd7

Please sign in to comment.