Skip to content

Commit

Permalink
Process: Allow None for input ports that are not required
Browse files Browse the repository at this point in the history
Python functions and process functions accept `None` for arguments
that define that as a default without any issues. That is to say,
the following works just fine:

    from aiida.engine import calcfunction

    @calcfunction
    def accepts_none(a=None):
        pass

    accepts_none()
    accepts_none(None)

For a `Process`, however, `None` is not accepted even if the port is not
required. This can be annoying for users because when writing wrappers
around a process launcher, they will have to manually check for one of
the inputs being `None` and remove it before submitting the input
dictionary.

Here we update the `InputPort` constructor to automatically add the
`NoneType` to the `valid_type` argument if the port is marked as
`required=False`. This way, non-required ports will accept `None`
without raising an exception during input validation.
  • Loading branch information
sphuber committed Nov 2, 2022
1 parent 51c9939 commit ae79c89
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 3 deletions.
9 changes: 9 additions & 0 deletions aiida/engine/processes/ports.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ def __init__(self, *args, **kwargs) -> None:
' It is advised to use a lambda instead, e.g.: `default=lambda: orm.Int(5)`.'.format(args[0])
warnings.warn(UserWarning(message)) # pylint: disable=no-member

# If the port is not required and defines ``valid_type``, automatically add ``None`` as a valid type
valid_type = kwargs.get('valid_type', ())

if not isinstance(valid_type, (tuple, list)):
valid_type = [valid_type]

if not kwargs.get('required', True) and valid_type:
kwargs['valid_type'] = tuple(valid_type) + (type(None),)

super().__init__(*args, **kwargs)

def get_description(self) -> Dict[str, str]:
Expand Down
6 changes: 6 additions & 0 deletions docs/source/topics/processes/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ To signal that the value is invalid and to have a validation error raised, simpl
The ``valid_type`` can define a single type, or a tuple of valid types.

.. versionadded:: 2.1

If a port is marked as optional through ``required=False`` and defines ``valid_type``, the port will also accept ``None`` as values, whereas before this would raise validation error.
This is accomplished by automatically adding the ``NoneType`` to the ``valid_type`` tuple.
Ports that do not define a ``valid_type`` are not affected.

.. note::

Note that by default all ports are required, but specifying a default value implies that the input is not required and as such specifying ``required=False`` is not necessary in that case.
Expand Down
29 changes: 29 additions & 0 deletions tests/engine/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,32 @@ def test_input_validation_storable_nodes():
"""
with pytest.raises(ValueError):
run(TestValidateDynamicNamespaceProcess, **{'namespace': {'a': 1}})


class TestNotRequiredNoneProcess(Process):
"""Process with an optional input port that should therefore also accept ``None``."""

_node_class = orm.WorkflowNode

@classmethod
def define(cls, spec):
super().define(spec)
spec.input('valid_type', valid_type=orm.Int, required=False)
spec.input('any_type', required=False)


@pytest.mark.usefixtures('clear_database_before_test')
def test_not_required_accepts_none():
"""Test that a port that is not required, accepts ``None``."""
from aiida.engine.utils import instantiate_process
from aiida.manage import get_manager

runner = get_manager().get_runner()

process = instantiate_process(runner, TestNotRequiredNoneProcess, valid_type=None, any_type=None)
assert process.inputs.valid_type is None
assert process.inputs.any_type is None

process = instantiate_process(runner, TestNotRequiredNoneProcess, valid_type=orm.Int(1), any_type=orm.Bool(True))
assert process.inputs.valid_type == orm.Int(1)
assert process.inputs.any_type == orm.Bool(True)
6 changes: 3 additions & 3 deletions tests/sphinxext/test_workchain/test_workchain_build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<paragraph>
A demo workchain to show how the workchain auto-documentation works.
</paragraph>
<paragraph><strong>Inputs:</strong><bullet_list bullet="*"><list_item><literal_strong>metadata</literal_strong>, <emphasis>Namespace</emphasis><details opened="False"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>call_link_label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – The label to use for the <title_reference>CALL</title_reference> link if the process is called by another process.</list_item><list_item><literal_strong>description</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – Description to set on the process node.</list_item><list_item><literal_strong>label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – Label to set on the process node.</list_item><list_item><literal_strong>store_provenance</literal_strong>, <emphasis>bool</emphasis>, optional, <emphasis>non_db</emphasis> – If set to <title_reference>False</title_reference> provenance will not be stored in the database.</list_item></bullet_list></details></list_item><list_item><literal_strong>nsp</literal_strong>, <emphasis>Namespace</emphasis> – A separate namespace, <literal>nsp</literal>.</list_item><list_item><literal_strong>nsp2</literal_strong>, <emphasis>Namespace</emphasis></list_item><list_item><literal_strong>x</literal_strong>, <emphasis>Float</emphasis>, required – First input argument.</list_item><list_item><literal_strong>y</literal_strong>, <emphasis>Namespace</emphasis><details opened="False"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>nested</literal_strong>, <emphasis>Namespace</emphasis> – A nested namespace.<details opened="False"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>a</literal_strong>, <emphasis>Int</emphasis>, required – An input in the nested namespace.</list_item></bullet_list></details></list_item><list_item><literal_strong>z</literal_strong>, <emphasis>Int</emphasis>, required – Input in a separate namespace.</list_item></bullet_list></details></list_item></bullet_list></paragraph>
<paragraph><strong>Inputs:</strong><bullet_list bullet="*"><list_item><literal_strong>metadata</literal_strong>, <emphasis>Namespace</emphasis><details opened="False"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>call_link_label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – The label to use for the <title_reference>CALL</title_reference> link if the process is called by another process.</list_item><list_item><literal_strong>description</literal_strong>, <emphasis>(str, NoneType)</emphasis>, optional, <emphasis>non_db</emphasis> – Description to set on the process node.</list_item><list_item><literal_strong>label</literal_strong>, <emphasis>(str, NoneType)</emphasis>, optional, <emphasis>non_db</emphasis> – Label to set on the process node.</list_item><list_item><literal_strong>store_provenance</literal_strong>, <emphasis>bool</emphasis>, optional, <emphasis>non_db</emphasis> – If set to <title_reference>False</title_reference> provenance will not be stored in the database.</list_item></bullet_list></details></list_item><list_item><literal_strong>nsp</literal_strong>, <emphasis>Namespace</emphasis> – A separate namespace, <literal>nsp</literal>.</list_item><list_item><literal_strong>nsp2</literal_strong>, <emphasis>Namespace</emphasis></list_item><list_item><literal_strong>x</literal_strong>, <emphasis>Float</emphasis>, required – First input argument.</list_item><list_item><literal_strong>y</literal_strong>, <emphasis>Namespace</emphasis><details opened="False"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>nested</literal_strong>, <emphasis>Namespace</emphasis> – A nested namespace.<details opened="False"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>a</literal_strong>, <emphasis>Int</emphasis>, required – An input in the nested namespace.</list_item></bullet_list></details></list_item><list_item><literal_strong>z</literal_strong>, <emphasis>Int</emphasis>, required – Input in a separate namespace.</list_item></bullet_list></details></list_item></bullet_list></paragraph>
<paragraph><strong>Outputs:</strong><bullet_list bullet="*"><list_item><literal_strong>z</literal_strong>, <emphasis>Bool</emphasis>, required – Output of the demoworkchain.</list_item></bullet_list></paragraph>
<paragraph><strong>Outline:</strong><literal_block force="False" language="default" linenos="False" xml:space="preserve">start
while(some_check)
Expand Down Expand Up @@ -47,7 +47,7 @@ finalize</literal_block></paragraph>
<paragraph>
A demo workchain to show how the workchain auto-documentation works.
</paragraph>
<paragraph><strong>Inputs:</strong><bullet_list bullet="*"><list_item><literal_strong>metadata</literal_strong>, <emphasis>Namespace</emphasis><details opened="True"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>call_link_label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – The label to use for the <title_reference>CALL</title_reference> link if the process is called by another process.</list_item><list_item><literal_strong>description</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – Description to set on the process node.</list_item><list_item><literal_strong>label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – Label to set on the process node.</list_item><list_item><literal_strong>store_provenance</literal_strong>, <emphasis>bool</emphasis>, optional, <emphasis>non_db</emphasis> – If set to <title_reference>False</title_reference> provenance will not be stored in the database.</list_item></bullet_list></details></list_item><list_item><literal_strong>nsp</literal_strong>, <emphasis>Namespace</emphasis> – A separate namespace, <literal>nsp</literal>.</list_item><list_item><literal_strong>nsp2</literal_strong>, <emphasis>Namespace</emphasis></list_item><list_item><literal_strong>x</literal_strong>, <emphasis>Float</emphasis>, required – First input argument.</list_item><list_item><literal_strong>y</literal_strong>, <emphasis>Namespace</emphasis><details opened="True"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>nested</literal_strong>, <emphasis>Namespace</emphasis> – A nested namespace.<details opened="True"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>a</literal_strong>, <emphasis>Int</emphasis>, required – An input in the nested namespace.</list_item></bullet_list></details></list_item><list_item><literal_strong>z</literal_strong>, <emphasis>Int</emphasis>, required – Input in a separate namespace.</list_item></bullet_list></details></list_item></bullet_list></paragraph>
<paragraph><strong>Inputs:</strong><bullet_list bullet="*"><list_item><literal_strong>metadata</literal_strong>, <emphasis>Namespace</emphasis><details opened="True"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>call_link_label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – The label to use for the <title_reference>CALL</title_reference> link if the process is called by another process.</list_item><list_item><literal_strong>description</literal_strong>, <emphasis>(str, NoneType)</emphasis>, optional, <emphasis>non_db</emphasis> – Description to set on the process node.</list_item><list_item><literal_strong>label</literal_strong>, <emphasis>(str, NoneType)</emphasis>, optional, <emphasis>non_db</emphasis> – Label to set on the process node.</list_item><list_item><literal_strong>store_provenance</literal_strong>, <emphasis>bool</emphasis>, optional, <emphasis>non_db</emphasis> – If set to <title_reference>False</title_reference> provenance will not be stored in the database.</list_item></bullet_list></details></list_item><list_item><literal_strong>nsp</literal_strong>, <emphasis>Namespace</emphasis> – A separate namespace, <literal>nsp</literal>.</list_item><list_item><literal_strong>nsp2</literal_strong>, <emphasis>Namespace</emphasis></list_item><list_item><literal_strong>x</literal_strong>, <emphasis>Float</emphasis>, required – First input argument.</list_item><list_item><literal_strong>y</literal_strong>, <emphasis>Namespace</emphasis><details opened="True"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>nested</literal_strong>, <emphasis>Namespace</emphasis> – A nested namespace.<details opened="True"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>a</literal_strong>, <emphasis>Int</emphasis>, required – An input in the nested namespace.</list_item></bullet_list></details></list_item><list_item><literal_strong>z</literal_strong>, <emphasis>Int</emphasis>, required – Input in a separate namespace.</list_item></bullet_list></details></list_item></bullet_list></paragraph>
<paragraph><strong>Outputs:</strong><bullet_list bullet="*"><list_item><literal_strong>z</literal_strong>, <emphasis>Bool</emphasis>, required – Output of the demoworkchain.</list_item></bullet_list></paragraph>
<paragraph><strong>Outline:</strong><literal_block force="False" language="default" linenos="False" xml:space="preserve">start
while(some_check)
Expand All @@ -64,7 +64,7 @@ finalize</literal_block></paragraph>
<paragraph>
Here we check that the directive works even if the outline is empty.
</paragraph>
<paragraph><strong>Inputs:</strong><bullet_list bullet="*"><list_item><literal_strong>metadata</literal_strong>, <emphasis>Namespace</emphasis><details opened="False"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>call_link_label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – The label to use for the <title_reference>CALL</title_reference> link if the process is called by another process.</list_item><list_item><literal_strong>description</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – Description to set on the process node.</list_item><list_item><literal_strong>label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – Label to set on the process node.</list_item><list_item><literal_strong>store_provenance</literal_strong>, <emphasis>bool</emphasis>, optional, <emphasis>non_db</emphasis> – If set to <title_reference>False</title_reference> provenance will not be stored in the database.</list_item></bullet_list></details></list_item><list_item><literal_strong>x</literal_strong>, <emphasis>Float</emphasis>, required – First input argument.</list_item></bullet_list></paragraph>
<paragraph><strong>Inputs:</strong><bullet_list bullet="*"><list_item><literal_strong>metadata</literal_strong>, <emphasis>Namespace</emphasis><details opened="False"><summary>Namespace Ports</summary><bullet_list bullet="*"><list_item><literal_strong>call_link_label</literal_strong>, <emphasis>str</emphasis>, optional, <emphasis>non_db</emphasis> – The label to use for the <title_reference>CALL</title_reference> link if the process is called by another process.</list_item><list_item><literal_strong>description</literal_strong>, <emphasis>(str, NoneType)</emphasis>, optional, <emphasis>non_db</emphasis> – Description to set on the process node.</list_item><list_item><literal_strong>label</literal_strong>, <emphasis>(str, NoneType)</emphasis>, optional, <emphasis>non_db</emphasis> – Label to set on the process node.</list_item><list_item><literal_strong>store_provenance</literal_strong>, <emphasis>bool</emphasis>, optional, <emphasis>non_db</emphasis> – If set to <title_reference>False</title_reference> provenance will not be stored in the database.</list_item></bullet_list></details></list_item><list_item><literal_strong>x</literal_strong>, <emphasis>Float</emphasis>, required – First input argument.</list_item></bullet_list></paragraph>
<paragraph><strong>Outputs:</strong><paragraph>None defined.</paragraph></paragraph>
</desc_content>
</desc>
Expand Down

0 comments on commit ae79c89

Please sign in to comment.