Skip to content

Commit 6b4e098

Browse files
committed
Merge remote-tracking branch 'origin/master' into adi611-patch-testpsij-1
2 parents c4dbb9b + 0245cdc commit 6b4e098

File tree

8 files changed

+142
-33
lines changed

8 files changed

+142
-33
lines changed

.github/workflows/testslurm.yml

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ on:
88

99
jobs:
1010
build:
11+
strategy:
12+
matrix:
13+
python-version: [3.8.16, 3.9.16, 3.10.9, 3.11.5]
14+
fail-fast: false
1115
runs-on: ubuntu-latest
1216
env:
13-
DOCKER_IMAGE: giovtorres/docker-centos7-slurm:latest
17+
DOCKER_IMAGE: adi611/docker-centos7-slurm:23.02.1
1418

1519
steps:
1620
- name: Disable etelemetry
@@ -24,10 +28,7 @@ jobs:
2428
- name: Display previous jobs with sacct
2529
run: |
2630
echo "Allowing ports/daemons time to start" && sleep 10
27-
docker exec slurm bash -c "sacctmgr -i add cluster name=linux \
28-
&& supervisorctl restart slurmdbd \
29-
&& supervisorctl restart slurmctld \
30-
&& sacctmgr -i add account none,test Cluster=linux Description='none' Organization='none'"
31+
docker exec slurm bash -c "sacctmgr -i add account none,test Cluster=linux Description='none' Organization='none'"
3132
docker exec slurm bash -c "sacct && sinfo && squeue" 2&> /dev/null
3233
if [ $? -ne 0 ]; then
3334
echo "Slurm docker image error"
@@ -38,12 +39,16 @@ jobs:
3839
docker exec slurm bash -c "echo $NO_ET"
3940
docker exec slurm bash -c "ls -la && echo list top level dir"
4041
docker exec slurm bash -c "ls -la /pydra && echo list pydra dir"
41-
docker exec slurm bash -c "pip3.9 install --upgrade pip && pip3.9 install -e /pydra[test] && python3.9 -c 'import pydra; print(pydra.__version__)'"
42+
if [[ "${{ matrix.python-version }}" == "3.11.5" ]]; then
43+
docker exec slurm bash -c "CONFIGURE_OPTS=\"-with-openssl=/opt/openssl\" pyenv install -v 3.11.5"
44+
fi
45+
docker exec slurm bash -c "pyenv global ${{ matrix.python-version }}"
46+
docker exec slurm bash -c "pip install --upgrade pip && pip install -e /pydra[test] && python -c 'import pydra; print(pydra.__version__)'"
4247
- name: Run pytest
4348
run: |
44-
docker exec slurm bash -c "pytest --color=yes -vs -n auto --cov pydra --cov-config /pydra/.coveragerc --cov-report xml:/pydra/cov.xml --doctest-modules /pydra/pydra/ -k 'not test_audit_prov and not test_audit_prov_messdir_1 and not test_audit_prov_messdir_2 and not test_audit_prov_wf and not test_audit_all'"
49+
docker exec slurm bash -c "pytest --color=yes -vs --cov pydra --cov-config /pydra/.coveragerc --cov-report xml:/pydra/cov.xml --doctest-modules /pydra/pydra/ -k 'not test_audit_prov and not test_audit_prov_messdir_1 and not test_audit_prov_messdir_2 and not test_audit_prov_wf and not test_audit_all'"
4550
- name: Upload to codecov
4651
run: |
47-
docker exec slurm bash -c "pip3.9 install urllib3==1.26.6"
52+
docker exec slurm bash -c "pip install urllib3==1.26.6"
4853
docker exec slurm bash -c "codecov --root /pydra -f /pydra/cov.xml -F unittests"
4954
docker rm -f slurm

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ repos:
99
- id: check-yaml
1010
- id: check-added-large-files
1111
- repo: https://github.com/psf/black
12-
rev: 23.7.0
12+
rev: 23.9.1
1313
hooks:
1414
- id: black
1515
- repo: https://github.com/codespell-project/codespell

.zenodo.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@
7575
"name": "Vaillant, Ghislain",
7676
"orcid": "0000-0003-0267-3033"
7777
},
78+
{
79+
"affiliation": "Indian Institute of Information Technology Kalyani",
80+
"name": "Agarwal, Aditya",
81+
"orcid": "0009-0008-2639-5334"
82+
},
7883
{
7984
"affiliation": "MIT, HMS",
8085
"name": "Ghosh, Satrajit",

pydra/engine/tests/test_shelltask.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,14 +2460,14 @@ def test_wf_shell_cmd_3a(plugin, tmp_path):
24602460
assert res.output.cp_file.fspath.exists()
24612461

24622462

2463-
def test_wf_shell_cmd_state_1(plugin):
2463+
def test_wf_shell_cmd_state_1(plugin, tmp_path):
24642464
"""a workflow with 2 tasks and splitter on the wf level,
24652465
first one has input with output_file_template (str, uses wf.lzin),
24662466
that is passed to the second task
24672467
"""
2468-
wf = Workflow(name="wf", input_spec=["cmd1", "cmd2", "args"]).split(
2469-
"args", args=["newfile_1.txt", "newfile_2.txt"]
2470-
)
2468+
wf = Workflow(
2469+
name="wf", input_spec=["cmd1", "cmd2", "args"], cache_dir=tmp_path
2470+
).split("args", args=["newfile_1.txt", "newfile_2.txt"])
24712471

24722472
wf.inputs.cmd1 = "touch"
24732473
wf.inputs.cmd2 = "cp"
@@ -2820,7 +2820,7 @@ def gather_output(field, output_dir):
28202820

28212821

28222822
@pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
2823-
def test_shell_cmd_outputspec_5a(plugin, results_function):
2823+
def test_shell_cmd_outputspec_5a(plugin, results_function, tmp_path):
28242824
"""
28252825
customised output_spec, adding files to the output,
28262826
using a function to collect output, the function is saved in the field metadata
@@ -2842,7 +2842,9 @@ def gather_output(executable, output_dir):
28422842
],
28432843
bases=(ShellOutSpec,),
28442844
)
2845-
shelly = ShellCommandTask(name="shelly", executable=cmd, output_spec=my_output_spec)
2845+
shelly = ShellCommandTask(
2846+
name="shelly", executable=cmd, output_spec=my_output_spec, cache_dir=tmp_path
2847+
)
28462848

28472849
res = results_function(shelly, plugin)
28482850
assert res.output.stdout == ""
@@ -2874,7 +2876,7 @@ def gather_output(executable, output_dir, ble):
28742876

28752877

28762878
@pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
2877-
def test_shell_cmd_outputspec_5c(plugin, results_function):
2879+
def test_shell_cmd_outputspec_5c(plugin, results_function, tmp_path):
28782880
"""
28792881
Customised output spec defined as a class,
28802882
using a static function to collect output files.
@@ -2893,6 +2895,7 @@ def gather_output(executable, output_dir):
28932895
name="shelly",
28942896
executable=["touch", "newfile_tmp1.txt", "newfile_tmp2.txt"],
28952897
output_spec=SpecInfo(name="Output", bases=(MyOutputSpec,)),
2898+
cache_dir=tmp_path,
28962899
)
28972900

28982901
res = results_function(shelly, plugin)
@@ -3177,7 +3180,7 @@ def get_stderr(stderr):
31773180
)
31783181

31793182
shelly = ShellCommandTask(
3180-
name="shelly", executable=cmd, output_spec=my_output_spec
3183+
name="shelly", executable=cmd, output_spec=my_output_spec, cache_dir=tmp_path
31813184
).split("args", args=args)
31823185

31833186
results = results_function(shelly, plugin)
@@ -3248,6 +3251,7 @@ def get_lowest_directory(directory_path):
32483251
executable=cmd,
32493252
output_spec=my_output_spec,
32503253
resultsDir="outdir",
3254+
cache_dir=tmp_path,
32513255
).split("args", args=args)
32523256

32533257
results_function(shelly, plugin)

pydra/utils/hash.py

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
# import stat
55
import struct
6+
import typing as ty
67
from collections.abc import Mapping
78
from functools import singledispatch
89
from hashlib import blake2b
10+
import logging
911

1012
# from pathlib import Path
1113
from typing import (
@@ -14,10 +16,11 @@
1416
NewType,
1517
Sequence,
1618
Set,
17-
_SpecialForm,
1819
)
1920
import attrs.exceptions
2021

22+
logger = logging.getLogger("pydra")
23+
2124
try:
2225
from typing import Protocol
2326
except ImportError:
@@ -88,7 +91,8 @@ def hash_single(obj: object, cache: Cache) -> Hash:
8891
h = blake2b(digest_size=16, person=b"pydra-hash")
8992
for chunk in bytes_repr(obj, cache):
9093
h.update(chunk)
91-
cache[objid] = Hash(h.digest())
94+
hsh = cache[objid] = Hash(h.digest())
95+
logger.debug("Hash of %s object is %s", obj, hsh)
9296
return cache[objid]
9397

9498

@@ -102,15 +106,14 @@ def __bytes_repr__(self, cache: Cache) -> Iterator[bytes]:
102106
def bytes_repr(obj: object, cache: Cache) -> Iterator[bytes]:
103107
cls = obj.__class__
104108
yield f"{cls.__module__}.{cls.__name__}:{{".encode()
105-
try:
109+
dct: Dict[str, ty.Any]
110+
if attrs.has(type(obj)):
111+
# Drop any attributes that aren't used in comparisons by default
112+
dct = attrs.asdict(obj, recurse=False, filter=lambda a, _: bool(a.eq))
113+
elif hasattr(obj, "__slots__"):
114+
dct = {attr: getattr(obj, attr) for attr in obj.__slots__}
115+
else:
106116
dct = obj.__dict__
107-
except AttributeError as e:
108-
# Attrs creates slots classes by default, so we add this here to handle those
109-
# cases
110-
try:
111-
dct = attrs.asdict(obj, recurse=False) # type: ignore
112-
except attrs.exceptions.NotAnAttrsClassError:
113-
raise TypeError(f"Cannot hash {obj} as it is a slots class") from e
114117
yield from bytes_repr_mapping_contents(dct, cache)
115118
yield b"}"
116119

@@ -224,10 +227,34 @@ def bytes_repr_dict(obj: dict, cache: Cache) -> Iterator[bytes]:
224227
yield b"}"
225228

226229

227-
@register_serializer(_SpecialForm)
230+
@register_serializer(ty._GenericAlias)
231+
@register_serializer(ty._SpecialForm)
228232
@register_serializer(type)
229233
def bytes_repr_type(klass: type, cache: Cache) -> Iterator[bytes]:
230-
yield f"type:({klass.__module__}.{klass.__name__})".encode()
234+
def type_name(tp):
235+
try:
236+
name = tp.__name__
237+
except AttributeError:
238+
name = tp._name
239+
return name
240+
241+
yield b"type:("
242+
origin = ty.get_origin(klass)
243+
if origin:
244+
yield f"{origin.__module__}.{type_name(origin)}[".encode()
245+
for arg in ty.get_args(klass):
246+
if isinstance(
247+
arg, list
248+
): # sometimes (e.g. Callable) the args of a type is a list
249+
yield b"["
250+
yield from (b for t in arg for b in bytes_repr_type(t, cache))
251+
yield b"]"
252+
else:
253+
yield from bytes_repr_type(arg, cache)
254+
yield b"]"
255+
else:
256+
yield f"{klass.__module__}.{type_name(klass)}".encode()
257+
yield b")"
231258

232259

233260
@register_serializer(list)

pydra/utils/tests/test_hash.py

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
import attrs
66
import pytest
7-
7+
import typing as ty
8+
from fileformats.application import Zip, Json
89
from ..hash import Cache, UnhashableError, bytes_repr, hash_object, register_serializer
910

1011

@@ -134,6 +135,17 @@ def __init__(self, x):
134135
assert re.match(rb".*\.MyClass:{str:1:x=.{16}}", obj_repr)
135136

136137

138+
def test_bytes_repr_slots_obj():
139+
class MyClass:
140+
__slots__ = ("x",)
141+
142+
def __init__(self, x):
143+
self.x = x
144+
145+
obj_repr = join_bytes_repr(MyClass(1))
146+
assert re.match(rb".*\.MyClass:{str:1:x=.{16}}", obj_repr)
147+
148+
137149
def test_bytes_repr_attrs_slots():
138150
@attrs.define
139151
class MyClass:
@@ -143,11 +155,67 @@ class MyClass:
143155
assert re.match(rb".*\.MyClass:{str:1:x=.{16}}", obj_repr)
144156

145157

146-
def test_bytes_repr_type():
158+
def test_bytes_repr_attrs_no_slots():
159+
@attrs.define(slots=False)
160+
class MyClass:
161+
x: int
162+
163+
obj_repr = join_bytes_repr(MyClass(1))
164+
assert re.match(rb".*\.MyClass:{str:1:x=.{16}}", obj_repr)
165+
166+
167+
def test_bytes_repr_type1():
147168
obj_repr = join_bytes_repr(Path)
148169
assert obj_repr == b"type:(pathlib.Path)"
149170

150171

172+
def test_bytes_repr_type1a():
173+
obj_repr = join_bytes_repr(Zip[Json])
174+
assert obj_repr == rb"type:(fileformats.application.archive.Json__Zip)"
175+
176+
177+
def test_bytes_repr_type2():
178+
T = ty.TypeVar("T")
179+
180+
class MyClass(ty.Generic[T]):
181+
pass
182+
183+
obj_repr = join_bytes_repr(MyClass[int])
184+
assert (
185+
obj_repr == b"type:(pydra.utils.tests.test_hash.MyClass[type:(builtins.int)])"
186+
)
187+
188+
189+
def test_bytes_special_form1():
190+
obj_repr = join_bytes_repr(ty.Union[int, float])
191+
assert obj_repr == b"type:(typing.Union[type:(builtins.int)type:(builtins.float)])"
192+
193+
194+
def test_bytes_special_form2():
195+
obj_repr = join_bytes_repr(ty.Any)
196+
assert re.match(rb"type:\(typing.Any\)", obj_repr)
197+
198+
199+
def test_bytes_special_form3():
200+
obj_repr = join_bytes_repr(ty.Optional[Path])
201+
assert (
202+
obj_repr == b"type:(typing.Union[type:(pathlib.Path)type:(builtins.NoneType)])"
203+
)
204+
205+
206+
def test_bytes_special_form4():
207+
obj_repr = join_bytes_repr(ty.Type[Path])
208+
assert obj_repr == b"type:(builtins.type[type:(pathlib.Path)])"
209+
210+
211+
def test_bytes_special_form5():
212+
obj_repr = join_bytes_repr(ty.Callable[[Path, int], ty.Tuple[float, str]])
213+
assert obj_repr == (
214+
b"type:(collections.abc.Callable[[type:(pathlib.Path)type:(builtins.int)]"
215+
b"type:(builtins.tuple[type:(builtins.float)type:(builtins.str)])])"
216+
)
217+
218+
151219
def test_recursive_object():
152220
a = []
153221
b = [a]

pydra/utils/tests/test_typing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ...engine.specs import File, LazyOutField
99
from ..typing import TypeParser
1010
from pydra import Workflow
11-
from fileformats.serialization import Json
11+
from fileformats.application import Json
1212
from .utils import (
1313
generic_func_task,
1414
GenericShellTask,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "flit_scm:buildapi"
66
name = "pydra"
77
description = "Pydra dataflow engine"
88
readme = "README.rst"
9-
requires-python = ">=3.8"
9+
requires-python = ">=3.8, !=3.11.1"
1010
dependencies = [
1111
"attrs >=19.1.0",
1212
"cloudpickle >=2.0.0",

0 commit comments

Comments
 (0)