def test_lambda_default(self): """Test that an input port can specify a lambda as a default.""" port_namespace = PortNamespace('base') # Defining lambda for default that returns incorrect type should not except at construction port_namespace['port'] = InputPort('port', valid_type=Int, default=lambda: 'string') # However, pre processing the namespace, which shall evaluate the default followed by validation will fail inputs = port_namespace.pre_process({}) self.assertIsNotNone(port_namespace.validate(inputs)) # Passing an explicit value for the port will forego the default and validation on returned inputs should pass inputs = port_namespace.pre_process({'port': Int(5)}) self.assertIsNone(port_namespace.validate(inputs)) # Redefining the port, this time with a correct default port_namespace['port'] = InputPort('port', valid_type=Int, default=lambda: Int(5)) # Pre processing the namespace shall evaluate the default and return the int node inputs = port_namespace.pre_process({}) self.assertIsInstance(inputs['port'], Int) self.assertEqual(inputs['port'].value, 5) # Passing an explicit value for the port will forego the default inputs = port_namespace.pre_process({'port': Int(3)}) self.assertIsInstance(inputs['port'], Int) self.assertEqual(inputs['port'].value, 3)
def test_with_non_db(self): """Ports inserted to a `PortNamespace` should inherit the `non_db` attribute if not explicitly set.""" namespace_non_db = True port_namespace = PortNamespace('namespace', non_db=namespace_non_db) # When explicitly set upon port construction, value should not be inherited even when different port = InputPort('storable', non_db=False) port_namespace['storable'] = port self.assertEqual(port.non_db, False) port = InputPort('not_storable', non_db=True) port_namespace['not_storable'] = port self.assertEqual(port.non_db, True) # If not explicitly defined, it should inherit from parent namespace port = InputPort('not_storable') port_namespace['not_storable'] = port self.assertEqual(port.non_db, namespace_non_db)
def __init__(self, port_namespace: PortNamespace) -> None: """Dynamically construct the get and set properties for the ports of the given port namespace. For each port in the given port namespace a get and set property will be constructed dynamically and added to the ProcessBuilderNamespace. The docstring for these properties will be defined by calling str() on the Port, which should return the description of the Port. :param port_namespace: the inputs PortNamespace for which to construct the builder """ # pylint: disable=super-init-not-called self._port_namespace = port_namespace self._valid_fields = [] self._data = {} # The name and port objects have to be passed to the defined functions as defaults for # their arguments, because this way the content at the time of defining the method is # saved. If they are used directly in the body, it will try to capture the value from # its enclosing scope at the time of being called. for name, port in port_namespace.items(): self._valid_fields.append(name) if isinstance(port, PortNamespace): self._data[name] = ProcessBuilderNamespace(port) def fgetter(self, name=name): return self._data.get(name) elif port.has_default(): def fgetter(self, name=name, default=port.default): # type: ignore # pylint: disable=cell-var-from-loop return self._data.get(name, default) else: def fgetter(self, name=name): return self._data.get(name, None) def fsetter(self, value, name=name): self._data[name] = value fgetter.__doc__ = str(port) getter = property(fgetter) getter.setter(fsetter) # pylint: disable=too-many-function-args setattr(self.__class__, name, getter)
def test_validate_port_name(self): """This test will ensure that illegal port names will raise a `ValueError` when trying to add it.""" port = InputPort('port') port_namespace = PortNamespace('namespace') illegal_port_names = [ 'two__underscores', 'three___underscores', '_leading_underscore', 'trailing_underscore_', 'non_numeric_%', 'including.period', 'disallowed👻unicodecharacters', 'white space', 'das-hes', ] for port_name in illegal_port_names: with self.assertRaises(ValueError): port_namespace[port_name] = port
def test_serialize_type_check(self): """Test that `serialize` will include full port namespace in exception message.""" base_namespace = 'base' nested_namespace = 'some.nested.namespace' port_namespace = PortNamespace(base_namespace) port_namespace.create_port_namespace(nested_namespace) with self.assertRaisesRegex( TypeError, f'.*{base_namespace}.*{nested_namespace}.*'): port_namespace.serialize( {'some': { 'nested': { 'namespace': Dict() } }})
def test_serialize_type_check(self): """Test that `serialize` will include full port namespace in exception message.""" base_namespace = 'base' nested_namespace = 'some.nested.namespace' port_namespace = PortNamespace(base_namespace) port_namespace.create_port_namespace(nested_namespace) # pylint: disable=deprecated-method # The `assertRaisesRegexp` method is deprecated in python 3 but assertRaisesRegex` does not exist in python 2 with self.assertRaisesRegexp( TypeError, '.*{}.*{}.*'.format(base_namespace, nested_namespace)): port_namespace.serialize( {'some': { 'nested': { 'namespace': {Dict()} } }})