Skip to content

Commit

Permalink
renamed spec to definition across the board
Browse files Browse the repository at this point in the history
  • Loading branch information
tclose committed Dec 31, 2024
1 parent 70edf85 commit 9594e75
Show file tree
Hide file tree
Showing 29 changed files with 289 additions and 267 deletions.
2 changes: 1 addition & 1 deletion docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Release Notes

* refactoring template formatting for ``input_spec``
* fixing issues with input fields with extension (and using them in templates)
* adding simple validators to input spec (using ``attr.validator``)
* adding simple validators to input definition (using ``attr.validator``)
* adding ``create_dotfile`` for workflows, that creates graphs as dotfiles (can convert to other formats if dot available)
* adding a simple user guide with ``input_spec`` description
* expanding docstrings for ``State``, ``audit`` and ``messenger``
Expand Down
6 changes: 3 additions & 3 deletions docs/input_spec.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Let's start from the previous example:
In order to create an input definition, a new `SpecInfo` object has to be created.
The field `name` specifies the type of the spec and it should be always "Input" for
The field `name` specifies the type of the definition and it should be always "Input" for
the input definition.
The field `bases` specifies the "base definition" you want to use (can think about it as a
`parent class`) and it will usually contains `ShellDef` only, unless you want to build on top of
Expand Down Expand Up @@ -150,12 +150,12 @@ In the example we used multiple keys in the metadata dictionary including `help_
If `True` a path will be consider as a path inside the container (and not as a local path).
`output_file_template` (`str`):
If provided, the field is treated also as an output field and it is added to the output spec.
If provided, the field is treated also as an output field and it is added to the output definition.
The template can use other fields, e.g. `{file1}`.
Used in order to create an output definition.
`output_field_name` (`str`, used together with `output_file_template`)
If provided the field is added to the output spec with changed name.
If provided the field is added to the output definition with changed name.
Used in order to create an output definition.
`keep_extension` (`bool`, default: `True`):
Expand Down
4 changes: 2 additions & 2 deletions docs/output_spec.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ a customized `output_spec` can be used, e.g.:
Similarly as for `input_spec`, in order to create an output definition,
a new `SpecInfo` object has to be created.
The field `name` specifies the type of the spec and it should be always "Output" for
The field `name` specifies the type of the definition and it should be always "Output" for
the output definition.
The field `bases` specifies the "base definition" you want to use (can think about it as a
`parent class`) and it will usually contains `ShellOutDef` only, unless you want to build on top of
Expand Down Expand Up @@ -69,7 +69,7 @@ The metadata dictionary for `output_spec` can include:
The template can use other fields, e.g. `{file1}`. The same as in `input_spec`.

`output_field_name` (`str`, used together with `output_file_template`)
If provided the field is added to the output spec with changed name.
If provided the field is added to the output definition with changed name.
The same as in `input_spec`.

`keep_extension` (`bool`, default: `True`):
Expand Down
2 changes: 1 addition & 1 deletion new-docs/source/tutorial/shell.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@
" outputs={\"out_file_size\": get_file_size},\n",
")\n",
"\n",
"# Parameterise the task spec\n",
"# Parameterise the task definition\n",
"cp_with_size = CpWithSize(in_file=File.sample())\n",
"\n",
"# Run the command\n",
Expand Down
4 changes: 2 additions & 2 deletions pydra/design/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ def make_task_spec(
if name is None:
raise ValueError("name must be provided if klass is not")
if klass is not None and issubclass(klass, TaskDef):
raise ValueError(f"Cannot change type of spec {klass} to {spec_type}")
raise ValueError(f"Cannot change type of definition {klass} to {spec_type}")
bases = tuple(bases)
# Ensure that TaskDef is a base class
if not any(issubclass(b, spec_type) for b in bases):
Expand Down Expand Up @@ -497,7 +497,7 @@ def make_outputs_spec(
if not any(issubclass(b, spec_type) for b in bases):
if out_spec_bases := [b for b in bases if issubclass(b, TaskOutputs)]:
raise ValueError(
f"Cannot make {spec_type} output spec from {out_spec_bases} bases"
f"Cannot make {spec_type} output definition from {out_spec_bases} bases"
)
outputs_bases = bases + (spec_type,)
if reserved_names := [n for n in outputs if n in spec_type.RESERVED_FIELD_NAMES]:
Expand Down
14 changes: 7 additions & 7 deletions pydra/design/boutiques.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def _download_spec(zenodo_id):
searcher = Searcher(zenodo_id, exact_match=True)
hits = searcher.zenodo_search().json()["hits"]["hits"]
if len(hits) == 0:
raise Exception(f"can't find zenodo spec for {zenodo_id}")
raise Exception(f"can't find zenodo definition for {zenodo_id}")
elif len(hits) > 1:
raise Exception(f"too many hits for {zenodo_id}")
else:
Expand All @@ -146,8 +146,8 @@ def _download_spec(zenodo_id):


def _prepare_input_spec(bosh_spec: dict[str, ty.Any], names_subset=None):
"""creating input spec from the zenodo file
if name_subset provided, only names from the subset will be used in the spec
"""creating input definition from the zenodo file
if name_subset provided, only names from the subset will be used in the definition
"""
binputs = bosh_spec["inputs"]
input_keys = {}
Expand Down Expand Up @@ -185,13 +185,13 @@ def _prepare_input_spec(bosh_spec: dict[str, ty.Any], names_subset=None):
)
input_keys[input["value-key"]] = "{" + f"{name}" + "}"
if names_subset:
raise RuntimeError(f"{names_subset} are not in the zenodo input spec")
raise RuntimeError(f"{names_subset} are not in the zenodo input definition")
return fields, input_keys


def _prepare_output_spec(bosh_spec: dict[str, ty.Any], input_keys, names_subset=None):
"""creating output spec from the zenodo file
if name_subset provided, only names from the subset will be used in the spec
"""creating output definition from the zenodo file
if name_subset provided, only names from the subset will be used in the definition
"""
boutputs = bosh_spec["output-files"]
fields = []
Expand Down Expand Up @@ -219,5 +219,5 @@ def _prepare_output_spec(bosh_spec: dict[str, ty.Any], input_keys, names_subset=
)

if names_subset:
raise RuntimeError(f"{names_subset} are not in the zenodo output spec")
raise RuntimeError(f"{names_subset} are not in the zenodo output definition")
return fields
4 changes: 2 additions & 2 deletions pydra/design/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

@attrs.define
class arg(Arg):
"""Argument of a Python task spec
"""Argument of a Python task definition
Parameters
----------
Expand Down Expand Up @@ -58,7 +58,7 @@ class arg(Arg):

@attrs.define
class out(Out):
"""Output of a Python task spec
"""Output of a Python task definition
Parameters
----------
Expand Down
4 changes: 2 additions & 2 deletions pydra/design/tests/test_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ def func(a: int) -> float:
python.arg(name="function", type=ty.Callable, default=func),
]
assert outputs == [python.out(name="out", type=float)]
spec = SampleDef(a=1)
result = spec()
definition = SampleDef(a=1)
result = definition()
assert result.output.out == 2.0
with pytest.raises(TypeError):
SampleDef(a=1.5)
Expand Down
4 changes: 2 additions & 2 deletions pydra/design/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

@attrs.define
class arg(Arg):
"""Argument of a workflow task spec
"""Argument of a workflow task definition
Parameters
----------
Expand Down Expand Up @@ -63,7 +63,7 @@ class arg(Arg):

@attrs.define
class out(Out):
"""Output of a workflow task spec
"""Output of a workflow task definition
Parameters
----------
Expand Down
62 changes: 33 additions & 29 deletions pydra/engine/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ class Task:
_references = None # List of references for a task

name: str
spec: TaskDef
definition: TaskDef

_inputs: dict[str, ty.Any] | None = None

def __init__(
self,
spec,
definition,
name: str | None = None,
audit_flags: AuditFlag = AuditFlag.NONE,
cache_dir=None,
Expand Down Expand Up @@ -137,12 +137,12 @@ def __init__(
if Task._etelemetry_version_data is None:
Task._etelemetry_version_data = check_latest_version()

self.spec = spec
self.definition = definition
self.name = name

self.input_names = [
field.name
for field in attr.fields(type(self.spec))
for field in attr.fields(type(self.definition))
if field.name not in ["_func", "_graph_checksums"]
]

Expand All @@ -159,11 +159,11 @@ def __init__(
raise ValueError(f"Unknown input set {inputs!r}")
inputs = self._input_sets[inputs]

self.spec = attr.evolve(self.spec, **inputs)
self.definition = attr.evolve(self.definition, **inputs)

# checking if metadata is set properly
self.spec._check_resolved()
self.spec._check_rules()
self.definition._check_resolved()
self.definition._check_rules()
self._output = {}
self._result = {}
# flag that says if node finished all jobs
Expand Down Expand Up @@ -195,11 +195,11 @@ def __str__(self):

def __getstate__(self):
state = self.__dict__.copy()
state["spec"] = cp.dumps(state["spec"])
state["definition"] = cp.dumps(state["definition"])
return state

def __setstate__(self, state):
state["spec"] = cp.loads(state["spec"])
state["definition"] = cp.loads(state["definition"])
self.__dict__.update(state)

def help(self, returnhelp=False):
Expand All @@ -225,7 +225,7 @@ def checksum(self):
and to create nodes checksums needed for graph checksums
(before the tasks have inputs etc.)
"""
input_hash = self.spec._hash
input_hash = self.definition._hash
self._checksum = create_checksum(self.__class__.__name__, input_hash)
return self._checksum

Expand Down Expand Up @@ -262,7 +262,7 @@ def output_names(self):
"""Get the names of the outputs from the task's output_spec
(not everything has to be generated, see generated_output_names).
"""
return [f.name for f in attr.fields(self.spec.Outputs)]
return [f.name for f in attr.fields(self.definition.Outputs)]

@property
def generated_output_names(self):
Expand Down Expand Up @@ -373,10 +373,12 @@ def inputs(self) -> dict[str, ty.Any]:
from pydra.utils.typing import TypeParser

self._inputs = {
k: v for k, v in attrs_values(self.spec).items() if not k.startswith("_")
k: v
for k, v in attrs_values(self.definition).items()
if not k.startswith("_")
}
map_copyfiles = {}
for fld in list_fields(self.spec):
for fld in list_fields(self.definition):
name = fld.name
value = self._inputs[name]
if value is not attr.NOTHING and TypeParser.contains_type(
Expand All @@ -392,7 +394,9 @@ def inputs(self) -> dict[str, ty.Any]:
if value is not copied_value:
map_copyfiles[name] = copied_value
self._inputs.update(
template_update(self.spec, self.output_dir, map_copyfiles=map_copyfiles)
template_update(
self.definition, self.output_dir, map_copyfiles=map_copyfiles
)
)
return self._inputs

Expand Down Expand Up @@ -436,7 +440,7 @@ def _run(self, rerun=False, environment=None):
try:
self.audit.monitor()
self._run_task(environment=environment)
result.output = self.spec.Outputs.from_task(self)
result.output = self.definition.Outputs.from_task(self)
except Exception:
etype, eval, etr = sys.exc_info()
traceback = format_exception(etype, eval, etr)
Expand Down Expand Up @@ -483,7 +487,7 @@ def get_input_el(self, ind):
for inp in set(self.input_names):
if f"{self.name}.{inp}" in input_ind:
inputs_dict[inp] = self._extract_input_el(
inputs=self.spec,
inputs=self.definition,
inp_nm=inp,
ind=input_ind[f"{self.name}.{inp}"],
)
Expand All @@ -506,7 +510,7 @@ def pickle_task(self):
def done(self):
"""Check whether the tasks has been finalized and all outputs are stored."""
# if any of the field is lazy, there is no need to check results
if has_lazy(self.spec):
if has_lazy(self.definition):
return False
_result = self.result()
if self.state:
Expand Down Expand Up @@ -588,7 +592,7 @@ def result(self, state_index=None, return_inputs=False):
self._errored = True
if return_inputs is True or return_inputs == "val":
inputs_val = {
f"{self.name}.{inp}": getattr(self.spec, inp)
f"{self.name}.{inp}": getattr(self.definition, inp)
for inp in self.input_names
}
return (inputs_val, result)
Expand All @@ -600,19 +604,19 @@ def result(self, state_index=None, return_inputs=False):

def _reset(self):
"""Reset the connections between inputs and LazyFields."""
for field in attrs_fields(self.spec):
for field in attrs_fields(self.definition):
if field.name in self.inp_lf:
setattr(self.spec, field.name, self.inp_lf[field.name])
setattr(self.definition, field.name, self.inp_lf[field.name])
if is_workflow(self):
for task in self.graph.nodes:
task._reset()

def _check_for_hash_changes(self):
hash_changes = self.spec._hash_changes()
hash_changes = self.definition._hash_changes()
details = ""
for changed in hash_changes:
field = getattr(attr.fields(type(self.spec)), changed)
val = getattr(self.spec, changed)
field = getattr(attr.fields(type(self.definition)), changed)
val = getattr(self.definition, changed)
field_type = type(val)
if issubclass(field.type, FileSet):
details += (
Expand Down Expand Up @@ -644,8 +648,8 @@ def _check_for_hash_changes(self):
"Input values and hashes for '%s' %s node:\n%s\n%s",
self.name,
type(self).__name__,
self.spec,
self.spec._hashes,
self.definition,
self.definition._hashes,
)

SUPPORTED_COPY_MODES = FileSet.CopyMode.any
Expand Down Expand Up @@ -753,12 +757,12 @@ def checksum(self):
(before the tasks have inputs etc.)
"""
# if checksum is called before run the _graph_checksums is not ready
if is_workflow(self) and self.spec._graph_checksums is attr.NOTHING:
self.spec._graph_checksums = {
if is_workflow(self) and self.definition._graph_checksums is attr.NOTHING:
self.definition._graph_checksums = {
nd.name: nd.checksum for nd in self.graph_sorted
}

input_hash = self.spec.hash
input_hash = self.definition.hash
if not self.state:
self._checksum = create_checksum(
self.__class__.__name__, self._checksum_wf(input_hash)
Expand Down Expand Up @@ -1037,7 +1041,7 @@ async def _run_task(self, submitter, rerun=False, environment=None):
# logger.info("Added %s to %s", self.output_spec, self)

def _collect_outputs(self):
output_klass = self.spec.Outputs
output_klass = self.definition.Outputs
output = output_klass(
**{f.name: attr.NOTHING for f in attr.fields(output_klass)}
)
Expand Down
Loading

0 comments on commit 9594e75

Please sign in to comment.