Skip to content

Commit

Permalink
ORM: fix deprecation warning always being shown in link managers (#5011)
Browse files Browse the repository at this point in the history
The link managers for the `Node` class which are used for the `inputs`
and `outputs` attributes and facilitate the tab-completion of incoming
and outgoing links, was recently changed to deprecate the direct use of
double underscores in link labels in favor of treating them as normal
nested dictionaries. The deprecation warning was thrown whenever the
label contained a double underscore, but this would therefore also
trigger on dunder methods, which is not desirable behaviour.

This inaccuracy manifested itself in the deprecation method being
printed even when just activating the tab-completion on `node.outputs`
or `node.inputs` without even specifying a label with a double
underscore. It is not fully understood how `_get_node_by_link_label` is
called in doing this, but it seems some caching mechanism is calling the
`__wrapped__` attribute on the link manager, which in turn triggers the
deprecation warning. An additional clause in the condition to exclude
dunder methods fixes the behaviour.

Cherry-pick: 53c5564
  • Loading branch information
sphuber committed Aug 9, 2021
1 parent dd4075e commit 80f0d7c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 5 deletions.
16 changes: 14 additions & 2 deletions aiida/orm/utils/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ def _get_node_by_link_label(self, label):
try:
node = attribute_dict[label]
except KeyError as exception:
if '__' in label:
# Check whether the label contains a double underscore, in which case we want to warn the user that this is
# deprecated. However, we need to exclude labels that corresponds to dunder methods, i.e., those that start
# and end with a double underscore.
if '__' in label and not (label.startswith('__') and label.endswith('__')):
import functools
import warnings
from aiida.common.warnings import AiidaDeprecationWarning
Expand All @@ -98,7 +101,16 @@ def _get_node_by_link_label(self, label):
'Support for double underscores will be removed in the future.', AiidaDeprecationWarning
) # pylint: disable=no-member
namespaces = label.split('__')
return functools.reduce(lambda d, namespace: d.get(namespace), namespaces, attribute_dict)
try:
return functools.reduce(lambda d, namespace: d.get(namespace), namespaces, attribute_dict)
except TypeError as exc:
# This can be raised if part of the `namespaces` correspond to an actual leaf node, but is treated
# like a namespace
raise NotExistent from exc
except AttributeError as exc:
# This will be raised if any of the intermediate namespaces don't exist, and so the label node does
# not exist.
raise NotExistent from exc
raise NotExistent from exception

return node
Expand Down
27 changes: 24 additions & 3 deletions tests/orm/utils/test_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ def test_link_manager_with_nested_namespaces(clear_database_before_test):
out1.add_incoming(calc, link_type=LinkType.CREATE, link_label='nested__sub__namespace')
out1.store()

out2 = orm.Data()
out2.add_incoming(calc, link_type=LinkType.CREATE, link_label='remote_folder')
out2.store()

# Check that the recommended way of dereferencing works
assert calc.inputs.nested.sub.namespace.uuid == inp1.uuid
assert calc.outputs.nested.sub.namespace.uuid == out1.uuid
Expand All @@ -164,10 +168,27 @@ def test_link_manager_with_nested_namespaces(clear_database_before_test):
assert calc.inputs.nested__sub__namespace.uuid == inp1.uuid
assert calc.outputs.nested__sub__namespace.uuid == out1.uuid

# Dunder methods should not invoke the deprecation warning
with pytest.warns(None) as record:
try:
calc.inputs.__name__
except AttributeError:
pass
assert not record

# Must raise a AttributeError, otherwise tab competion will not work
with pytest.raises(AttributeError):
_ = calc.outputs.nested.not_existent
for attribute in ['not_existent', 'not__existent__nested']:
with pytest.raises(AttributeError):
_ = getattr(calc.outputs.nested, attribute)

# Must raise a KeyError
for key in ['not_existent', 'not__existent__nested']:
with pytest.raises(KeyError):
_ = calc.outputs.nested[key]

# Note that `remote_folder` corresponds to an actual leaf node, but it is treated like an intermediate namespace
with pytest.raises(AttributeError):
_ = calc.outputs.remote_folder__namespace

with pytest.raises(KeyError):
_ = calc.outputs.nested['not_existent']
_ = calc.outputs['remote_folder__namespace']

0 comments on commit 80f0d7c

Please sign in to comment.