Repository: nipype/pydra

Issue:
Readable SpecInfos
The SpecInfo class is currently very inscrutable. An improved `__repr__` will be important for making development more friendly.

Discussion:

Patch:
diff --git a/pydra/engine/helpers.py b/pydra/engine/helpers.py
index 4835dfe9..1a441f1d 100644
--- a/pydra/engine/helpers.py
+++ b/pydra/engine/helpers.py
@@ -221,13 +221,29 @@ def make_klass(spec):
                 if isinstance(item[1], attr._make._CountingAttr):
                     newfields[item[0]] = item[1]
                 else:
-                    newfields[item[0]] = attr.ib(type=item[1])
+                    newfields[item[0]] = attr.ib(repr=False, type=item[1])
             else:
-                if isinstance(item[2], attr._make._CountingAttr):
-                    raise ValueError("Three part should not have attr")
-                    # newfields[item[0]] = item[2]
+                if (
+                    any([isinstance(ii, attr._make._CountingAttr) for ii in item])
+                    or len(item) > 4
+                ):
+                    raise ValueError(
+                        "syntax not valid, you can use (name, attr), "
+                        "(name, type, default), (name, type, default, metadata)"
+                        "or (name, type, metadata)"
+                    )
                 else:
-                    newfields[item[0]] = attr.ib(item[2], type=item[1])
+                    if len(item) == 3:
+                        name, tp = item[:2]
+                        if isinstance(item[-1], dict) and "help_string" in item[-1]:
+                            mdata = item[-1]
+                            newfields[name] = attr.ib(type=tp, metadata=mdata)
+                        else:
+                            dflt = item[-1]
+                            newfields[name] = attr.ib(type=tp, default=dflt)
+                    elif len(item) == 4:
+                        name, tp, dflt, mdata = item
+                        newfields[name] = attr.ib(type=tp, default=dflt, metadata=mdata)
         fields = newfields
     return attr.make_class(spec.name, fields, bases=spec.bases, kw_only=True)
 


Test patch:
diff --git a/pydra/engine/tests/test_shelltask.py b/pydra/engine/tests/test_shelltask.py
index 5d291579..011e83c7 100644
--- a/pydra/engine/tests/test_shelltask.py
+++ b/pydra/engine/tests/test_shelltask.py
@@ -392,6 +392,32 @@ def test_shell_cmd_inputspec_3(plugin, results_function):
 @pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
 @pytest.mark.parametrize("plugin", Plugins)
 def test_shell_cmd_inputspec_3a(plugin, results_function):
+    """  mandatory field added to fields, value provided
+        using shorter syntax for input spec (no attr.ib)
+    """
+    cmd_exec = "echo"
+    hello = "HELLO"
+    my_input_spec = SpecInfo(
+        name="Input",
+        fields=[
+            ("text", str, {"position": 1, "help_string": "text", "mandatory": True})
+        ],
+        bases=(ShellSpec,),
+    )
+
+    # separate command into exec + args
+    shelly = ShellCommandTask(
+        name="shelly", executable=cmd_exec, text=hello, input_spec=my_input_spec
+    )
+    assert shelly.inputs.executable == cmd_exec
+    assert shelly.cmdline == "echo HELLO"
+    res = results_function(shelly, plugin)
+    assert res.output.stdout == "HELLO\n"
+
+
+@pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
+@pytest.mark.parametrize("plugin", Plugins)
+def test_shell_cmd_inputspec_3b(plugin, results_function):
     """  mandatory field added to fields, value provided after init"""
     cmd_exec = "echo"
     hello = "HELLO"
@@ -421,7 +447,7 @@ def test_shell_cmd_inputspec_3a(plugin, results_function):
 
 
 @pytest.mark.parametrize("plugin", Plugins)
-def test_shell_cmd_inputspec_3b_exception(plugin):
+def test_shell_cmd_inputspec_3c_exception(plugin):
     """  mandatory field added to fields, value is not provided, so exception is raised """
     cmd_exec = "echo"
     my_input_spec = SpecInfo(
@@ -511,6 +537,31 @@ def test_shell_cmd_inputspec_4(plugin, results_function):
 @pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
 @pytest.mark.parametrize("plugin", Plugins)
 def test_shell_cmd_inputspec_4a(plugin, results_function):
+    """  mandatory field added to fields, value provided
+        using shorter syntax for input spec (no attr.ib)
+    """
+    cmd_exec = "echo"
+    my_input_spec = SpecInfo(
+        name="Input",
+        fields=[("text", str, "Hello", {"position": 1, "help_string": "text"})],
+        bases=(ShellSpec,),
+    )
+
+    # separate command into exec + args
+    shelly = ShellCommandTask(
+        name="shelly", executable=cmd_exec, input_spec=my_input_spec
+    )
+
+    assert shelly.inputs.executable == cmd_exec
+    assert shelly.cmdline == "echo Hello"
+
+    res = results_function(shelly, plugin)
+    assert res.output.stdout == "Hello\n"
+
+
+@pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
+@pytest.mark.parametrize("plugin", Plugins)
+def test_shell_cmd_inputspec_4b(plugin, results_function):
     """  mandatory field added to fields, value provided """
     cmd_exec = "echo"
     my_input_spec = SpecInfo(
@@ -1170,6 +1221,33 @@ def test_shell_cmd_inputspec_state_1(plugin, results_function):
     assert res[1].output.stdout == "hi\n"
 
 
+@pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
+@pytest.mark.parametrize("plugin", Plugins)
+def test_shell_cmd_inputspec_state_1a(plugin, results_function):
+    """  adding state to the input from input_spec
+        using shorter syntax for input_spec (without default)
+    """
+    cmd_exec = "echo"
+    hello = ["HELLO", "hi"]
+    my_input_spec = SpecInfo(
+        name="Input",
+        fields=[
+            ("text", str, {"position": 1, "help_string": "text", "mandatory": True})
+        ],
+        bases=(ShellSpec,),
+    )
+
+    # separate command into exec + args
+    shelly = ShellCommandTask(
+        name="shelly", executable=cmd_exec, text=hello, input_spec=my_input_spec
+    ).split("text")
+    assert shelly.inputs.executable == cmd_exec
+
+    res = results_function(shelly, plugin)
+    assert res[0].output.stdout == "HELLO\n"
+    assert res[1].output.stdout == "hi\n"
+
+
 @pytest.mark.parametrize("results_function", [result_no_submitter, result_submitter])
 @pytest.mark.parametrize("plugin", Plugins)
 def test_shell_cmd_inputspec_state_2(plugin, results_function):