Skip to content

Commit 4151f10

Browse files
committed
Handle automatic operation registration in more cases.
Cleaned up the logic for expanding the Context operations map to handle cases where more than one operation is implemented in a non-native namespace. The previous logic erroneously considered this an error. Fixes #234 Future improvements could try to discover all operations the first time a new namespace is encountered, but this version continues to allow more granular manipulation of namespaces with Context.add_operation().
1 parent 5992245 commit 4151f10

File tree

1 file changed

+34
-29
lines changed

1 file changed

+34
-29
lines changed

src/gmx/context.py

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -759,47 +759,52 @@ def work(self, work):
759759
for e in workspec.elements:
760760
element = WorkElement.deserialize(workspec.elements[e])
761761

762-
if element.namespace not in {'gmxapi', 'gromacs'} and element.namespace not in self.__operations:
763-
# Non-built-in namespaces are treated as modules to import.
764-
try:
765-
element_module = importlib.import_module(element.namespace)
766-
except ImportError as e:
767-
raise exceptions.UsageError(
768-
'This context does not know how to invoke {} from {}. ImportError: {}'.format(
769-
element.operation,
762+
# Note: Non-built-in namespaces (non-native) are treated as modules to import.
763+
# Native namespaces may not be completely implemented in a particular version of a particular Context.
764+
if element.namespace in {'gmxapi', 'gromacs'}:
765+
assert element.namespace in self.__operations
766+
if not element.operation in self.__operations[element.namespace]:
767+
# The requested element is a built-in operation but not available in this Context.
768+
# element.namespace should be mapped, but not all operations are necessarily implemented.
769+
logger.error("Operation {} not found in map {}".format(element.operation,
770+
str(self.__operations)))
771+
# This check should be performed when deciding if the context is appropriate for the work.
772+
# If we are just going to use a try/catch block for this test, then we should differentiate
773+
# this exception from those raised due to incorrect usage.
774+
# The exception thrown here may evolve with https://github.com/kassonlab/gmxapi/issues/125
775+
raise exceptions.FeatureNotAvailableError(
776+
'Specified work cannot be performed due to unimplemented operation {}.{}.'.format(
770777
element.namespace,
771-
str(e)))
778+
element.operation))
779+
780+
else:
781+
assert element.namespace not in {'gmxapi', 'gromacs'}
772782

773783
# Don't leave an empty nested dictionary if we couldn't map the operation.
774784
if element.namespace in self.__operations:
775785
namespace_map = self.__operations[element.namespace]
776786
else:
777787
namespace_map = dict()
778788

789+
# Set or update namespace map iff we have something new.
779790
if element.operation not in namespace_map:
780791
try:
792+
element_module = importlib.import_module(element.namespace)
781793
element_operation = getattr(element_module, element.operation)
782-
namespace_map[element.operation] = element_operation
783-
except:
784-
raise exceptions.ApiError('Operation {} not found in {}.'.format(element.operation,
785-
element.namespace))
786-
# Set or update namespace map only if we have something to contribute.
794+
except ImportError as e:
795+
raise exceptions.UsageError(
796+
'Cannot find implementation for namespace {}. ImportError: {}'.format(
797+
element.namespace,
798+
str(e)))
799+
except AttributeError:
800+
raise exceptions.UsageError(
801+
'Cannot find factory for operation {}.{}'.format(
802+
element.namespace,
803+
element.operation
804+
)
805+
)
806+
namespace_map[element.operation] = element_operation
787807
self.__operations[element.namespace] = namespace_map
788-
else:
789-
# The requested element is a built-in operation or otherwise already configured.
790-
# element.namespace should be mapped, but not all operations are necessarily implemented.
791-
assert element.namespace in self.__operations
792-
if not element.operation in self.__operations[element.namespace]:
793-
logger.error("Operation {} not found in map {}".format(element.operation,
794-
str(self.__operations)))
795-
# This check should be performed when deciding if the context is appropriate for the work.
796-
# If we are just going to use a try/catch block for this test, then we should differentiate
797-
# this exception from those raised due to incorrect usage.
798-
# The exception thrown here may evolve with https://github.com/kassonlab/gmxapi/issues/125
799-
raise exceptions.ApiError(
800-
'Specified work cannot be performed due to unimplemented operation {}.{}.'.format(
801-
element.namespace,
802-
element.operation))
803808

804809
self.__work = workspec
805810

0 commit comments

Comments
 (0)