Skip to content

Commit f16343e

Browse files
committed
split-out unittests into new sub-packages/modules
1 parent 8df2898 commit f16343e

36 files changed

+2672
-2627
lines changed

.github/workflows/ci-cd.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ jobs:
181181
docker exec slurm bash -c "pip install --upgrade pip && pip install -e /pydra[test,psij] && python -c 'import pydra.engine; print(pydra.utils.__version__)'"
182182
- name: Run pytest
183183
run: |
184-
docker exec slurm bash -c "pytest /pydra/pydra/engine/tests/test_submitter.py --import-mode=importlib --rootdir /pydra/pydra --only-worker=slurm --color=yes -vs --cov pydra --cov-config /pydra/.coveragerc --cov-report xml:/pydra/cov.xml"
184+
docker exec slurm bash -c "pytest /pydra/pydra/workers/tests/test_worker.py --import-mode=importlib --rootdir /pydra/pydra --only-worker=slurm --color=yes -vs --cov pydra --cov-config /pydra/.coveragerc --cov-report xml:/pydra/cov.xml"
185185
- name: Upload coverage to Codecov
186186
uses: codecov/codecov-action@v2
187187
with:
@@ -261,7 +261,7 @@ jobs:
261261
# pip install --upgrade pip && pip install -e .[test] && python -c 'import pydra.engine; print(pydra.utils.__version__)'
262262
# - name: Run pytest
263263
# run: |
264-
# pytest pydra/engine/tests/test_submitter.py --import-mode=importlib --rootdir . --only-worker=sge --color=yes -vs --cov pydra --cov-config .coveragerc --cov-report xml:cov.xml
264+
# pytest pydra/workers/tests/test_worker.py --import-mode=importlib --rootdir . --only-worker=sge --color=yes -vs --cov pydra --cov-config .coveragerc --cov-report xml:cov.xml
265265
# - name: Upload coverage to Codecov
266266
# uses: codecov/codecov-action@v2
267267
# with:

pydra/compose/shell/task.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444

4545
@attrs.define(kw_only=True, auto_attribs=False, eq=False, repr=False)
46-
class Outputs(base.Outputs):
46+
class ShellOutputs(base.Outputs):
4747
"""Output task of a generic shell process."""
4848

4949
BASE_NAMES = ["return_code", "stdout", "stderr"]
@@ -216,7 +216,7 @@ def _resolve_value(
216216
return callable_(**call_args_val)
217217

218218

219-
ShellOutputsType = ty.TypeVar("OutputType", bound=Outputs)
219+
ShellOutputsType = ty.TypeVar("OutputType", bound=ShellOutputs)
220220

221221

222222
@state_array_support
@@ -508,3 +508,4 @@ def split_cmd(cmd: str | None):
508508

509509
# Alias ShellTask to Task so we can refer to it by shell.Task
510510
Task = ShellTask
511+
Outputs = ShellOutputs

pydra/compose/tests/test_shell.py renamed to pydra/compose/shell/tests/test_shell_fields.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import pytest
66
import cloudpickle as cp
77
from pydra.compose import shell
8-
from pydra.utils.general import task_fields
8+
from pydra.utils.general import task_fields, task_help, wrap_text
99
from pydra.compose.shell.builder import _InputPassThrough
1010
from fileformats.generic import File, Directory, FsObject
1111
from fileformats import text, image
@@ -984,6 +984,49 @@ def test_shell_missing_executable_dynamic():
984984
)
985985

986986

987+
def test_shell_help1():
988+
989+
Shelly = shell.define(
990+
"shelly <in_file:generic/file> <out|out_file:generic/file> --arg1 <arg1:int> "
991+
"--arg2 <arg2:float?> --opt-out <out|opt_out:File?>"
992+
)
993+
994+
assert task_help(Shelly) == [
995+
"----------------------------",
996+
"Help for Shell task 'shelly'",
997+
"----------------------------",
998+
"",
999+
"Inputs:",
1000+
"- executable: str | Sequence[str]; default = 'shelly'",
1001+
" the first part of the command, can be a string, e.g. 'ls', or a list, e.g.",
1002+
" ['ls', '-l', 'dirname']",
1003+
"- in_file: generic/file",
1004+
"- out_file: Path | bool; default = True",
1005+
] + wrap_text(shell.outarg.PATH_TEMPLATE_HELP).split("\n") + [
1006+
"- arg1: int ('--arg1')",
1007+
"- arg2: float | None; default = None ('--arg2')",
1008+
"- opt_out: Path | bool | None; default = None ('--opt-out')",
1009+
] + wrap_text(
1010+
shell.outarg.OPTIONAL_PATH_TEMPLATE_HELP
1011+
).split(
1012+
"\n"
1013+
) + [
1014+
"- additional_args: list[str | generic/file]; default-factory = list()",
1015+
" Additional free-form arguments to append to the end of the command.",
1016+
"",
1017+
"Outputs:",
1018+
"- out_file: generic/file",
1019+
"- opt_out: generic/file | None; default = None",
1020+
"- return_code: int",
1021+
" " + shell.Outputs.RETURN_CODE_HELP,
1022+
"- stdout: str",
1023+
" " + shell.Outputs.STDOUT_HELP,
1024+
"- stderr: str",
1025+
" " + shell.Outputs.STDERR_HELP,
1026+
"",
1027+
]
1028+
1029+
9871030
def list_entries(stdout):
9881031
return stdout.split("\n")[:-1]
9891032

pydra/engine/tests/test_shelltask.py renamed to pydra/compose/shell/tests/test_shell_run.py

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
File,
1313
Directory,
1414
)
15-
from pydra.utils.general import task_help, wrap_text
1615
from pydra.utils.typing import (
1716
MultiOutputFile,
1817
MultiInputObj,
@@ -3576,44 +3575,24 @@ class Outputs(shell.Outputs):
35763575
)
35773576

35783577

3579-
def test_shell_help1():
3578+
@no_win
3579+
def test_shell_cmd(tmpdir):
3580+
cmd = ["echo", "hail", "pydra"]
35803581

3582+
# all args given as executable
3583+
Shelly = shell.define(" ".join(cmd))
3584+
shelly = Shelly()
3585+
assert shelly.cmdline == " ".join(cmd)
3586+
outputs = shelly()
3587+
assert outputs.stdout == " ".join(cmd[1:]) + "\n"
3588+
3589+
# separate command into exec + args
35813590
Shelly = shell.define(
3582-
"shelly <in_file:generic/file> <out|out_file:generic/file> --arg1 <arg1:int> "
3583-
"--arg2 <arg2:float?> --opt-out <out|opt_out:File?>"
3591+
cmd[0], inputs=[shell.arg(name=a, default=a) for a in cmd[1:]]
35843592
)
3585-
3586-
assert task_help(Shelly) == [
3587-
"----------------------------",
3588-
"Help for Shell task 'shelly'",
3589-
"----------------------------",
3590-
"",
3591-
"Inputs:",
3592-
"- executable: str | Sequence[str]; default = 'shelly'",
3593-
" the first part of the command, can be a string, e.g. 'ls', or a list, e.g.",
3594-
" ['ls', '-l', 'dirname']",
3595-
"- in_file: generic/file",
3596-
"- out_file: Path | bool; default = True",
3597-
] + wrap_text(shell.outarg.PATH_TEMPLATE_HELP).split("\n") + [
3598-
"- arg1: int ('--arg1')",
3599-
"- arg2: float | None; default = None ('--arg2')",
3600-
"- opt_out: Path | bool | None; default = None ('--opt-out')",
3601-
] + wrap_text(
3602-
shell.outarg.OPTIONAL_PATH_TEMPLATE_HELP
3603-
).split(
3604-
"\n"
3605-
) + [
3606-
"- additional_args: list[str | generic/file]; default-factory = list()",
3607-
" Additional free-form arguments to append to the end of the command.",
3608-
"",
3609-
"Outputs:",
3610-
"- out_file: generic/file",
3611-
"- opt_out: generic/file | None; default = None",
3612-
"- return_code: int",
3613-
" " + shell.Outputs.RETURN_CODE_HELP,
3614-
"- stdout: str",
3615-
" " + shell.Outputs.STDOUT_HELP,
3616-
"- stderr: str",
3617-
" " + shell.Outputs.STDERR_HELP,
3618-
"",
3619-
]
3593+
shelly = Shelly()
3594+
assert shelly.executable == "echo"
3595+
assert shelly.cmdline == " ".join(cmd)
3596+
outputs = shelly()
3597+
assert outputs.return_code == 0
3598+
assert outputs.stdout == " ".join(cmd[1:]) + "\n"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import typing as ty
2+
from pydra.compose import shell
3+
from pydra.compose.shell.templating import argstr_formatting
4+
from pathlib import Path
5+
from unittest.mock import Mock
6+
from pydra.compose.shell.templating import template_update_single
7+
import os
8+
import shutil
9+
from pathlib import Path
10+
import random
11+
import platform
12+
import typing as ty
13+
import pytest
14+
import cloudpickle as cp
15+
from pydra.engine.submitter import Submitter
16+
from pydra.engine.job import Job
17+
from pydra.compose import workflow
18+
from fileformats.generic import Directory, File
19+
from pydra.engine.tests.utils import Multiply, RaiseXeq1
20+
from pydra.utils.general import position_sort
21+
from pydra.compose.shell.templating import parse_format_string
22+
from pydra.engine.job import save, load_and_run
23+
from pydra.workers.cf import get_available_cpus
24+
from pydra.utils.hash import hash_function
25+
26+
27+
@pytest.mark.parametrize(
28+
"pos_args",
29+
[
30+
[(2, "b"), (1, "a"), (3, "c")],
31+
[(-2, "b"), (1, "a"), (-1, "c")],
32+
[(None, "b"), (1, "a"), (-1, "c")],
33+
[(-3, "b"), (None, "a"), (-1, "c")],
34+
[(None, "b"), (1, "a"), (None, "c")],
35+
],
36+
)
37+
def test_position_sort(pos_args):
38+
final_args = position_sort(pos_args)
39+
assert final_args == ["a", "b", "c"]
40+
41+
42+
def test_parse_format_string1():
43+
assert parse_format_string("{a}") == {"a"}
44+
45+
46+
def test_parse_format_string2():
47+
assert parse_format_string("{abc}") == {"abc"}
48+
49+
50+
def test_parse_format_string3():
51+
assert parse_format_string("{a:{b}}") == {"a", "b"}
52+
53+
54+
def test_parse_format_string4():
55+
assert parse_format_string("{a:{b[2]}}") == {"a", "b"}
56+
57+
58+
def test_parse_format_string5():
59+
assert parse_format_string("{a.xyz[somekey].abc:{b[a][b].d[0]}}") == {"a", "b"}
60+
61+
62+
def test_parse_format_string6():
63+
assert parse_format_string("{a:05{b[a 2][b].e}}") == {"a", "b"}
64+
65+
66+
def test_parse_format_string7():
67+
assert parse_format_string(
68+
"{a1_field} {b2_field:02f} -test {c3_field[c]} -me {d4_field[0]}"
69+
) == {"a1_field", "b2_field", "c3_field", "d4_field"}
70+
71+
72+
def test_argstr_formatting():
73+
@shell.define
74+
class Shelly(shell.Task["Shelly.Outputs"]):
75+
a1_field: str
76+
b2_field: float
77+
c3_field: ty.Dict[str, str]
78+
d4_field: ty.List[str] = shell.arg(sep=" ")
79+
executable = "dummy"
80+
81+
class Outputs(shell.Outputs):
82+
pass
83+
84+
values = dict(a1_field="1", b2_field=2.0, c3_field={"c": "3"}, d4_field=["4"])
85+
assert (
86+
argstr_formatting(
87+
"{a1_field} {b2_field:02f} -test {c3_field[c]} -me {d4_field[0]}",
88+
values,
89+
)
90+
== "1 2.000000 -test 3 -me 4"
91+
)
92+
93+
94+
def test_template_formatting(tmp_path: Path):
95+
field = Mock()
96+
field.name = "grad"
97+
field.argstr = "--grad"
98+
field.path_template = ("{in_file}.bvec", "{in_file}.bval")
99+
field.keep_extension = False
100+
task = Mock()
101+
values = {"in_file": Path("/a/b/c/file.txt"), "grad": True}
102+
103+
assert template_update_single(
104+
field,
105+
task,
106+
values=values,
107+
output_dir=tmp_path,
108+
spec_type="input",
109+
) == [tmp_path / "file.bvec", tmp_path / "file.bval"]

pydra/compose/tests/test_python.py renamed to pydra/compose/tests/test_python_fields.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,27 @@ class Outputs:
400400
@staticmethod
401401
def function(a):
402402
return a + 1
403+
404+
405+
def test_task_repr():
406+
@python.define(outputs=["x", "y", "z"])
407+
def IdentityN3(x: int, y: int = 1, z: int = 2) -> tuple[int, int, int]:
408+
return x, y, z
409+
410+
assert repr(IdentityN3(x=1, y=2)) == "IdentityN3(x=1, y=2)"
411+
412+
413+
@attrs.define(auto_attribs=True)
414+
class A:
415+
x: int
416+
417+
418+
def test_object_input():
419+
"""Test function tasks with object inputs"""
420+
421+
@python.define
422+
def TestFunc(a: A):
423+
return a.x
424+
425+
outputs = TestFunc(a=A(x=7))()
426+
assert outputs.out == 7

pydra/engine/tests/test_numpy_examples.py renamed to pydra/compose/tests/test_python_numpy.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,16 @@ def test_task_numpyinput_2(tmp_path: Path):
9393
outputs = nn(cache_dir=tmp_path)
9494
assert outputs.out[0] == np.array(["VAL1"], dtype=object)
9595
assert outputs.out[1] == np.array(["VAL2"], dtype=object)
96+
97+
98+
def test_numpy_fft():
99+
"""checking if mark.task works for numpy functions"""
100+
np = pytest.importorskip("numpy")
101+
FFT = python.define(inputs={"a": np.ndarray}, outputs={"out": np.ndarray})(
102+
np.fft.fft
103+
)
104+
105+
arr = np.array([[1, 10], [2, 20]])
106+
fft = FFT(a=arr)
107+
outputs = fft()
108+
assert np.allclose(np.fft.fft(arr), outputs.out)

0 commit comments

Comments
 (0)