def _parse_envelope_value( self, name: str, envelope: Optional[Envelope] = None, parse_fun: Optional[Callable[[Any], Any]] = None, instance_lookup_fun: Optional[Callable[..., Optional[Any]]] = None) -> Any: """ Parse the envelope for the given `name`. If present in the envelope the extracted value will be tested / parsed by the given `parse_fun`. If no `parse_fun` is explicitly given, it will be determined if this instance provides a `parse_<name>`, `_parse_<name>` or a `__parse_<name>`. If yes the function is called; otherwise the value is returned as is. If the value is not present in the envelope the current instance will be probed for `<name>`, `_<name>` or `__<name>`. If an instance variable is present it will be returned unvalidated / unparsed (we assume that happened previously). If no variable is present simply `None` will be returned. Args: name: Name of the attribute to lookup in the envelope / instance. envelope: Envelope of the payload. parse_fun: Custom function to validate / parse. If not given it will be determined automagically. instance_lookup_fun: Custom function to perform instance variable lookups. If not given a reasonably default will be used. """ validator.is_instance(str, name=name) validator.is_instance(dict, allow_none=True, envelope=envelope) if parse_fun: validator.is_function(parse_fun=parse_fun) if instance_lookup_fun: validator.is_function(instance_lookup_fun=instance_lookup_fun) lookups = cast( Dict[str, str], utils.make_public_protected_private_attr_lookup(name, as_dict=True)) if envelope is None or name not in envelope: return (instance_lookup_fun(name) if instance_lookup_fun is not None else _lookup(self, list(lookups.values())) # pylint: disable=no-member ) val = envelope[name] try: if parse_fun is None: public_attr_name = lookups['public'] # pylint: disable=invalid-sequence-index fun_base = 'parse_' + public_attr_name parse_fun_names = utils.make_public_protected_private_attr_lookup( fun_base) parse_fun = _lookup(self, parse_fun_names) if parse_fun is None: return val return parse_fun(val) except (ValueError, TypeError): self.logger.exception("Cannot parse value for '%s' from envelope", name) return (instance_lookup_fun(name) if instance_lookup_fun is not None else _lookup(self, list(lookups.values())) # pylint: disable=no-member )
def make_public_protected_private_attr_lookup(attr_name: str, as_dict: bool = False) \ -> Union[Dict[str, str], List[str]]: """ Given an attribute name this function will generate names of public, private and protected attribute names. The order is of lookups is always the given attr_name first and then descending by visibility (public -> proctected -> private) Examples: >>> make_public_protected_private_attr_lookup('my_lookup') # Public attribute name ['my_lookup', '_my_lookup', '__my_lookup'] >>> make_public_protected_private_attr_lookup('_my_lookup') # Protected attribute name ['_my_lookup', 'my_lookup', '__my_lookup'] >>> make_public_protected_private_attr_lookup('__my_lookup') # Private attribute name ['__my_lookup', 'my_lookup', '_my_lookup'] >>> l = make_public_protected_private_attr_lookup('_my_lookup', as_dict=True) >>> list(l.keys()), list(l.values()) (['protected', 'public', 'private'], ['_my_lookup', 'my_lookup', '__my_lookup']) """ validator.is_instance(str, lookup_name=attr_name) as_dict = try_parse_bool(as_dict, default=False) if attr_name.startswith('__'): # __lookup, lookup, _lookup res = OrderedDict([('private', attr_name), ('public', attr_name[2:]), ('protected', attr_name[1:])]) elif attr_name.startswith('_'): # _lookup, lookup, __lookup res = OrderedDict([('protected', attr_name), ('public', attr_name[1:]), ('private', '_' + attr_name)]) else: # lookup, _lookup, __lookup res = OrderedDict([('public', attr_name), ('protected', '_' + attr_name), ('private', '__' + attr_name)]) return res if as_dict else list(res.values())
def __init__(self, directory=None, port=2121, user_pwd=None, events=None, max_cons=256, max_cons_ip=5, **kwargs): super().__init__(**kwargs) self.port = int(port) self.max_cons = int(max_cons) self.max_cons_ip = int(max_cons_ip) validator.is_instance(str, allow_none=True, directory=directory) if directory: validator.is_directory(directory=directory) self.directory = directory if not user_pwd: # No password -> anonymous access self.user = None elif isinstance(user_pwd, str): self.user = user_pwd self.password = '' elif is_iterable_but_no_str(user_pwd) and user_pwd: # at least one element self.user = user_pwd[0] if len(user_pwd) > 1: # Password too self.password = user_pwd[1] else: raise TypeError("Argument 'user_pwd' is expected to be a str (user) or a tuple of " "user and password.") self.events = make_list(events) or copy.copy(self.ALL_EVENTS) if self.events: validator.subset_of(self.ALL_EVENTS, events=self.events) self.server = None
def __init__(self, known_faces=None, known_faces_dir=None, unknown_label="Unknown", lazy=False, **kwargs): # known_faces -> mapping name -> list of files # known_faces_dir -> directory with known faces (filename -> name) # unknown_label -> Label for unknown faces super().__init__(**kwargs) one_not_none(known_faces=known_faces, known_faces_dir=known_faces_dir) self.known_faces = known_faces validator.is_instance(dict, allow_none=True, known_faces=self.known_faces) self.known_faces_dir = known_faces_dir and str(known_faces_dir) if self.known_faces_dir: validator.is_directory(known_faces_dir=self.known_faces_dir) if not lazy: self._configure() else: self.known_names = None self.known_encodings = None self.face_recognition = None self.unknown_label = str(unknown_label)
def __init__(self, discovery_prefix, component, config, object_id=None, node_id=None, **kwargs): super().__init__(**kwargs) self.discovery_prefix = str(discovery_prefix) validator.one_of(self.SUPPORTED_COMPONENTS, component=str(component)) self.component = str(component) self.object_id = self._parse_object_id(object_id) validator.is_instance(dict, config=config) self._config = config self.node_id = self._parse_node_id(node_id) self.configured = {}
def _call(self: 'Push', envelope: Optional[Envelope] = None, payload: Payload = None, **kwargs: Any) -> Payload: validator.is_instance(Push, self=self) parsed = self._parse_envelope_value(value, envelope=envelope) new_kwargs = {value: parsed, 'payload': payload} if envelope is not None: new_kwargs['envelope'] = envelope return fun(self, **{**new_kwargs, **kwargs})
async def execute( self, ident: str, payload: Payload, push: PushModel, result_callback: Optional[PushResultCallback] = None) -> None: """ Executes the given push (in an asynchronous context) by passing the specified payload. In concurrent environments there might be multiple executions in parallel. You may specify an `id` argument to identify related execution steps in the logs. Use the `result_callback` when the engine can take care of dependent pushes as well. The result and a dependent push will be passed via the callback. If the callback is not specified the PushExecute will execute them in a recursive manner. Args: ident (str): ID to identify related execution steps in the logs (makes sense in concurrent environments). payload (Any): The payload to pass to the push. push (PushModel): The push instance that has to process the payload. result_callback (callable): See explanation above. """ validator.is_instance(PushModel, push=push) if result_callback and not callable(result_callback): self.logger.warning( "Result callback is given, but is not a callable. Callback will be ignored." ) result_callback = None if push.unwrap and is_iterable_but_no_str(payload): # Payload unwrapping length = len(payload) self.logger.debug("[%s] Unwrapping payload to %s individual items", ident, str(length)) for item in payload: await self._internal(ident, item, push, result_callback) else: # Standard way await self._internal(ident, payload, push, result_callback)
def parse_envelope(value: str) -> PushFunction: """Decorator the parse the given value-key from the envelope. This is but a convenience decorator / wrapper for `_parse_envelope_value` of the `PushBase` class.""" validator.is_instance(str, value=value) def _inner(fun: PushFunction) -> PushFunction: validator.is_function(fun=fun) def _call(self: 'Push', envelope: Optional[Envelope] = None, payload: Payload = None, **kwargs: Any) -> Payload: validator.is_instance(Push, self=self) parsed = self._parse_envelope_value(value, envelope=envelope) new_kwargs = {value: parsed, 'payload': payload} if envelope is not None: new_kwargs['envelope'] = envelope return fun(self, **{**new_kwargs, **kwargs}) if asyncio.iscoroutinefunction(fun): @functools.wraps(fun) async def _wrapper(self: 'Push', envelope: Optional[Envelope] = None, payload: Payload = None, **kwargs: Any) -> Payload: return await _call(self, envelope, payload, **kwargs) return _wrapper return functools.wraps(fun)(_call) return _inner
def _call(self: 'Push', *args: Any, **kwargs: Any) -> Any: validator.is_instance(Push, self=self) kwargs.pop('envelope', None) return fun(self, *args, **kwargs)
def _call(self: 'Push', payload: Payload) -> Payload: validator.is_instance(Push, self=self) envelope, real_payload = self.envelope_payload(payload) return fun(self, envelope=envelope, payload=real_payload)
def load_plugin(plugin_path: str, plugin_type: Union[type, str], instantiate: bool = True, **kwargs: Any) -> Union[Plugin, Callable[..., Any]]: """ Loads a plugin by using a fully qualified identifier (<module_path>.<class_name>, e.g. pnp.plugins.pull.simple.Repeat). Args: plugin_path: Fully qualified path to plugin path. plugin_type: Base class the plugin has to extend / inherit from. instantiate: If True the class will be instantiated by passing the given **kwargs. Otherwise the class (not an object) is returned. **kwargs: Plugin arguments. Returns: If everything went fine an instantiated plugin is returned. Multiple things can go wrong: - Module not found (NamespaceNotFoundError) - Class not found (ClassNotFoundError) - Instantiation error (InvocationError) - Wrong plugin base type (PluginTypeError) """ validator.is_instance(str, plugin_path=plugin_path) validator.is_instance(type, str, plugin_type=plugin_type) if isinstance(plugin_type, str) and plugin_type != 'callable': raise ValueError( "When 'plugin_type' is str, the only allowed value is callable") k = plugin_path.rfind('.') if k > -1: # Namespace = everything before the last '.' namespace = plugin_path[:k] # Class name = everything after the last '.' clazz_name = plugin_path[k + 1:] else: namespace = 'builtins' clazz_name = plugin_path try: loaded_module = import_module(namespace) clazz = getattr(loaded_module, clazz_name) if isinstance(plugin_type, str) and plugin_type == 'callable': if not callable(clazz): raise PluginTypeError( "The plugin is requested to be a callable, but it is not.") else: if not issubclass(clazz, cast(type, plugin_type)): raise PluginTypeError( "The plugin is requested to inherit from '{}', but it does not." .format(plugin_type)) return cast(Plugin, clazz( **kwargs)) if instantiate else cast(Callable[..., Any], clazz) except AttributeError: raise ClassNotFoundError( 'Class {} was not found in namespace {}'.format( clazz_name, namespace)) from None except ImportError: raise NamespaceNotFoundError( "Namespace '{}' not found".format(namespace)) from None except TypeError as terr: raise InvocationError( "Invoked constructor from class '{}' in namespace '{}' failed". format(clazz_name, namespace)) from terr
def __init__(self, delta: timedelta): self.throttle_period = delta validator.is_instance(timedelta, delta=delta) self.time_of_last_call = datetime.min