Skip to content

Support for dataclasses at a surface level #634

@ghisvail

Description

@ghisvail

I am a big advocate for class-based definition of input and output task specifications over dynamic constructs via pydra.specs.SpecInfo(..., fields=[...]).

One annoying thing though is that class-based specifications aren't working with built-in dataclasses although they could from a pure semantic viewpoint. I understand the rationales (mostly pickling) for the transition at the implementation level, but I was wondering whether we could keep support for dataclasses at the surface API level.

For instance, let's take the following interface for fslreorder2std:

@attrs.define(slots=False, kw_only=True)
class FSLReorient2StdSpec(pydra.specs.ShellSpec):
    """Specificiations for fslreorient2std."""

    input_image: os.PathLike = attrs.field(
        metadata={
            "help_string": "input image",
            "mandatory": True,
            "argstr": "",
            "position": -2,
        }
    )

    output_image: str = attrs.field(
        metadata={
            "help_string": "output reoriented image",
            "argstr": "",
            "position": -1,
            "output_file_template": "{input_image}_r2std",
        }
    )

    output_matrix: str = attrs.field(
        metadata={
            "help_string": "output transformation matrix",
            "argstr": "-m",
            "output_file_template": "{input_image}_r2std.mat",
            "keep_extension": False,
        }
    )


class FSLReorient2Std(pydra.engine.ShellCommandTask):
    """Task definition for fslreorient2std."""

    executable = "fslreorient2std"

    input_spec = pydra.specs.SpecInfo(
        name="FSLReorient2StdInput", bases=(FSLReorient2StdSpec,)
    )

There really is nothing specific to attrs which is not covered by dataclasses:

diff --git a/pydra/tasks/fsl/fslreorient2std.py b/pydra/tasks/fsl/fslreorient2std.py
index c35a914..28b1724 100644
--- a/pydra/tasks/fsl/fslreorient2std.py
+++ b/pydra/tasks/fsl/fslreorient2std.py
@@ -15,18 +15,18 @@ Examples

 __all__ = ["FSLReorient2Std"]

-import os

-import attrs
+import dataclasses
+import os

 import pydra


-@attrs.define(slots=False, kw_only=True)
+@dataclasses.dataclass(kw_only=True)
 class FSLReorient2StdSpec(pydra.specs.ShellSpec):
     """Specificiations for fslreorient2std."""

-    input_image: os.PathLike = attrs.field(
+    input_image: os.PathLike = dataclasses.field(
         metadata={
             "help_string": "input image",
             "mandatory": True,
@@ -35,7 +35,7 @@ class FSLReorient2StdSpec(pydra.specs.ShellSpec):
         }
     )

-    output_image: str = attrs.field(
+    output_image: str = dataclasses.field(
         metadata={
             "help_string": "output reoriented image",
             "argstr": "",
@@ -44,7 +44,7 @@ class FSLReorient2StdSpec(pydra.specs.ShellSpec):
         }
     )

-    output_matrix: str = attrs.field(
+    output_matrix: str = dataclasses.field(
         metadata={
             "help_string": "output transformation matrix",
             "argstr": "-m",

As you can see the semantics are equivalent (not suprising considering their historical synergies), yet dataclasses has the benefits of being built-in and having its documentation right there with the rest of Python. Perhaps by transforming the dataclasses-based declaration into an attrs representation internally?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions