Skip to content

Commit 53c5564

Browse files
authored
ORM: fix deprecation warning always being shown in link managers (#5011)
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.
1 parent 58df44c commit 53c5564

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

aiida/orm/utils/managers.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ def _get_node_by_link_label(self, label):
8787
try:
8888
node = attribute_dict[label]
8989
except KeyError as exception:
90-
if '__' in label:
90+
# Check whether the label contains a double underscore, in which case we want to warn the user that this is
91+
# deprecated. However, we need to exclude labels that corresponds to dunder methods, i.e., those that start
92+
# and end with a double underscore.
93+
if '__' in label and not (label.startswith('__') and label.endswith('__')):
9194
import functools
9295
import warnings
9396
from aiida.common.warnings import AiidaDeprecationWarning
@@ -98,7 +101,16 @@ def _get_node_by_link_label(self, label):
98101
'Support for double underscores will be removed in `v3.0`.', AiidaDeprecationWarning
99102
) # pylint: disable=no-member
100103
namespaces = label.split('__')
101-
return functools.reduce(lambda d, namespace: d.get(namespace), namespaces, attribute_dict)
104+
try:
105+
return functools.reduce(lambda d, namespace: d.get(namespace), namespaces, attribute_dict)
106+
except TypeError as exc:
107+
# This can be raised if part of the `namespaces` correspond to an actual leaf node, but is treated
108+
# like a namespace
109+
raise NotExistent from exc
110+
except AttributeError as exc:
111+
# This will be raised if any of the intermediate namespaces don't exist, and so the label node does
112+
# not exist.
113+
raise NotExistent from exc
102114
raise NotExistent from exception
103115

104116
return node

tests/orm/utils/test_managers.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ def test_link_manager_with_nested_namespaces(clear_database_before_test):
152152
out1.add_incoming(calc, link_type=LinkType.CREATE, link_label='nested__sub__namespace')
153153
out1.store()
154154

155+
out2 = orm.Data()
156+
out2.add_incoming(calc, link_type=LinkType.CREATE, link_label='remote_folder')
157+
out2.store()
158+
155159
# Check that the recommended way of dereferencing works
156160
assert calc.inputs.nested.sub.namespace.uuid == inp1.uuid
157161
assert calc.outputs.nested.sub.namespace.uuid == out1.uuid
@@ -164,10 +168,27 @@ def test_link_manager_with_nested_namespaces(clear_database_before_test):
164168
assert calc.inputs.nested__sub__namespace.uuid == inp1.uuid
165169
assert calc.outputs.nested__sub__namespace.uuid == out1.uuid
166170

171+
# Dunder methods should not invoke the deprecation warning
172+
with pytest.warns(None) as record:
173+
try:
174+
calc.inputs.__name__
175+
except AttributeError:
176+
pass
177+
assert not record
178+
167179
# Must raise a AttributeError, otherwise tab competion will not work
168-
with pytest.raises(AttributeError):
169-
_ = calc.outputs.nested.not_existent
180+
for attribute in ['not_existent', 'not__existent__nested']:
181+
with pytest.raises(AttributeError):
182+
_ = getattr(calc.outputs.nested, attribute)
170183

171184
# Must raise a KeyError
185+
for key in ['not_existent', 'not__existent__nested']:
186+
with pytest.raises(KeyError):
187+
_ = calc.outputs.nested[key]
188+
189+
# Note that `remote_folder` corresponds to an actual leaf node, but it is treated like an intermediate namespace
190+
with pytest.raises(AttributeError):
191+
_ = calc.outputs.remote_folder__namespace
192+
172193
with pytest.raises(KeyError):
173-
_ = calc.outputs.nested['not_existent']
194+
_ = calc.outputs['remote_folder__namespace']

0 commit comments

Comments
 (0)