Esempio n. 1
0
 def test_to_snake_case(self):
     self.assertEqual(strings.to_snake_case("SomeCamelCaseName"),
                      "some_camel_case_name")
     self.assertEqual(strings.to_snake_case("SomeLARGEName"),
                      "some_large_name")
     self.assertEqual(strings.to_snake_case("Short"), "short")
     self.assertEqual(strings.to_snake_case("already_snake_case"),
                      "already_snake_case")
Esempio n. 2
0
    def __init__(self,
                 charge_values: Sequence[float],
                 charge_name: str = None) -> None:
        """
        The constructor of the ChargeValues class.

        If the charge name is None, the name of the class is used. If the JF factory included an alias in the classname,
        the alias is used.

        Parameters
        ----------
        charge_values : Sequence[float]
            The sequence of charges for each leaf node particle within a single tree.
        charge_name : str or None, optional
            The name of the charge.
        """
        log_init_arguments(logging.getLogger(__name__).debug,
                           self.__class__.__name__,
                           charge_values=charge_values,
                           charge_name=charge_name)
        # If no charge name is given, we want to use the alias of the .ini file which is included in the
        # __class__.__name__ property in the factory. get_alias() extracts this alias
        self._charge_name = (charge_name if charge_name is not None else
                             to_snake_case(get_alias(self.__class__.__name__)))
        self._charge_values = charge_values
Esempio n. 3
0
    def __init__(
        self,
        create: Sequence[str],
        trash: Sequence[str],
        event_handler: EventHandler,
        number_event_handlers: int,
        tag: str = None,
        activate: Sequence[str] = (),
        deactivate: Sequence[str] = ()
    ) -> None:
        """
        The constructor of the abstract tagger class.

        Parameters
        ----------
        create : Sequence[str]
            Sequence of tags to create after an event handler of this tagger has committed an event to the global state.
        trash : Sequence[str]
            Sequence of tags to trash after an event handler of this tagger has committed an event to the global state.
        event_handler : event_handler.EventHandler
            A single event handler instance.
        number_event_handlers : int
            Number of event handlers to prepare. The tagger will deepcopy the given event handler instance to create
            this number of event handlers.
        tag : str or None, optional
            Tag used in all four lists (also of other taggers). If None, the class name (or the alias set in the
            factory) will be used as the tag.
        activate : Sequence[str], optional
            Sequence of tags to activate after an event handler of this tagger has committed an event to the global
            state.
        deactivate : Sequence[str], optional
            Sequence of tags to deactivate after an event handler of this tagger has committed an event to the global
            state.
        """
        self._activates = activate
        self._deactivates = deactivate
        # Do this before calling super().__init__(), because Initializer class will overwrite public methods.
        self._deactivated_yield_identifiers_send_event_time = lambda separated_active_units: iter(
            ())
        self._activated_yield_identifiers_send_event_time = self.yield_identifiers_send_event_time
        super().__init__()
        # If no tag is given, we want to use the alias of the .ini file which is included in the __class__.__name__.
        # property in the factory. get_alias() extracts this alias
        self._tag = tag if tag is not None else to_snake_case(
            get_alias(self.__class__.__name__))
        self._creates = create
        self._trashes = trash
        self._number_event_handlers = number_event_handlers
        self._event_handler_to_copy = event_handler
        self._event_handlers = None
Esempio n. 4
0
 def _construct_methods_dictionary(
         self,
         starting_string: str) -> Dict[EventHandler, Callable[..., Any]]:
     """
     Construct a dictionary which maps from the event handlers onto the method defined in this class which starts
     with the given string and ends with the class name of the event handler or any base class of it.
     """
     functions_dictionary = {}
     for event_handler in self._event_handlers_list:
         # We don't have to worry about an alias set by the factory, since the original class is a subclass
         method = getattr(
             self, starting_string +
             strings.to_snake_case(event_handler.__class__.__name__), False)
         if not method:
             # No method found, check if the wanted method is defined for a subclass
             possible_methods = [
                 getattr(
                     self,
                     starting_string + strings.to_snake_case(subclass_name),
                     False) for subclass_name in _get_bases_names(
                         event_handler.__class__)
             ]
             possible_methods = [
                 method for method in possible_methods if method
             ]
             if len(possible_methods) == 0:
                 # No subclass defines a mediating method
                 continue
             if len(possible_methods) > 1:
                 raise MediatorError(
                     "More than one possible method for the event handler {0} "
                     "(read out from the list of base classes): {1}".format(
                         event_handler.__class__.__name__,
                         possible_methods))
             method = possible_methods[0]
         functions_dictionary[event_handler] = method
     return functions_dictionary
    def __init__(self, input_handler: InputHandler, output_handlers: Sequence[OutputHandler] = ()) -> None:
        """
        The constructor of the InputOutputHandler class.

        Parameters
        ----------
        input_handler : input_output_handler.input_handler.InputHandler
            The input handler.
        output_handlers : Sequence[input_output_handler.output_handler.OutputHandler]
            The sequence of output handlers.
        """
        log_init_arguments(logging.getLogger(__name__).debug, self.__class__.__name__,
                           input_handler=input_handler.__class__.__name__,
                           output_handlers=[output_handler.__class__.__name__ for output_handler in output_handlers])
        self._input_handler = input_handler
        # Event Handlers may refer to the alias (if there is one) of the .ini file which is included in the
        # __class__.__name__ property in the factory. get_alias() extracts this alias
        self._output_handlers_dictionary = {to_snake_case(get_alias(output_handler.__class__.__name__)): output_handler
                                            for output_handler in output_handlers}
Esempio n. 6
0
    def initialize_with_internal_states(
            self, internal_states: Sequence[InternalState]) -> None:
        """
        Initialize the tagger based on the initialized internal states.

        Adds a second initialize method relevant to the base Initializer class. This method is called once in the
        beginning of the run by the tag activator. However, this method does not call the initialize method of the
        Initializer class. Therefore, other public methods of this class can still not be called without raising an
        error after this method has been used. To finalize the initialization of this class, use the initialize method
        (which should be called after this method).

        This method extracts the internal state corresponding to the internal state label out of the sequence of
        internal states. If a tagger needs to further initialize its event handler in the self._event_handler_to_copy
        attribute (for example, with a cell system), it should extend this method.

        The subsequent call of the initialize method will then deepcopy the initialized event handler to create all
        event handlers for this tagger.

        Parameters
        ----------
        internal_states : Sequence[activator.internal_state.InternalState]
            Sequence of all initialized internal states in the activator.
        """
        super().initialize_with_internal_states(internal_states)
        relevant_internal_state = [
            state for state in internal_states
            if to_snake_case(get_alias(state.__class__.__name__)) ==
            self._internal_state_label
        ]
        if len(relevant_internal_state) == 0:
            raise ConfigurationError(
                "The given internal state label '{0}' does not exist!".format(
                    self._internal_state_label))
        if len(relevant_internal_state) > 1:
            raise ConfigurationError(
                "The given internal state label '{0}' exists more than once!".
                format(self._internal_state_label))
        self._internal_state = relevant_internal_state[0]
Esempio n. 7
0
def build_from_config(config: ConfigParser,
                      section: str,
                      package: str,
                      class_name: str = None) -> typing.Any:
    """
    Construct the object in a section of the configuration.

    The key/value pairs of the section must correspond to the arguments of the __init__ method of the class which is
    constructed. Keys and values must be in snake_case, section names in CamelCase.

    The parsed values must be converted to the correct types. The factory determines these types based on the required
    type hints in the __init__ method. Currently supported types are bool, float, int, and str. Similar to the
    configparser module, this factory recognizes booleans in the section from yes/no, on/off, true/false and 1/0. User
    defined classes are also be possible (the factory is then called recursively and the package is determined via the
    type hint). If the user defined class also needs some __init__ arguments, these must be defined in a separate
    section. Finally, typing.Sequence and typing.Iterable consisting of one of all these types is also possible. The
    factory creates a list in this case.

    It is possible to assign aliases for user defined classes within the configuration. In the values they should be
    given as 'alias (real_class_name)'. The arguments of the __init__ method for this class are then located in the
    section [Alias]. If the factory function is directly called with an alias section, the real class name must be
    specified in the class_name argument. For aliased classes, this factory will effectively subclass the real class and
    set __class__.__name__ to 'Alias (RealClassName)'. The alias (or the real class name if there is no alias) gets
    extracted from this attribute via the `get_alias` function of this module.

    User defined classes, which should be constructable by this factory, must be defined in their own module with the
    module name as the class name in snake_case. If the type hint refers to an abstract class, the inheriting class
    must be defined in the same package.

    If there exists a package with the same snake_case name as the class name (given via the section or class_name),
    the package is changed to this package before trying to import the module given by the class name.

    This function will append the section argument to the used_sections sequence of this module.

    Parameters
    ----------
    config : configparser.ConfigParser
        The parsed configuration file.
    section : str
        The section to construct in CamelCase.
    package : str
        The package where the object specified in the section is defined. The package must be part of the jellyfysh
        package.
    class_name : str or None, optional
        If the section is an alias, this is the real class name in CamelCase.

    Returns
    -------
    Any
        The constructed object.

    Raises
    ------
    ImportError
        If the module given by the class name does not exist in the given package.
    base.exceptions.ConfigurationError
        If the section does not specify all needed arguments of the __init__ method to construct the class.
        If the section specifies an argument which does not appear in the __init__ method of the class.
    TypeError
        If a type hint in the __init__ method is unsupported.
        If a type hint specifies a boolean but the corresponding value is not recognized as one.
    AssertionError
        If the package does not start with 'jellyfysh.'.
    """
    used_sections.append(section)
    if class_name is None:
        class_name = section

    try:
        section_config = config[section]
    except KeyError:
        section_config = {}

    module_name = strings.to_snake_case(class_name)
    assert package.startswith("jellyfysh.")
    if resource_isdir(
            "jellyfysh",
            strings.to_directory_path(package[len("jellyfysh."):]) + "/" +
            module_name):
        package = package + "." + module_name

    try:
        module = import_module(package + "." + module_name)
    except ImportError:
        raise ImportError(
            "Module '{0}' (set by section name) not existing in package '{1}'."
            .format(module_name, package))

    class_object = getattr(module, class_name)
    init_signature = signature(class_object.__init__)

    call_dictionary = {}
    for option in init_signature.parameters:
        # If option is not written in config, we assume it has a default value. If not, error is thrown on construction
        if option in section_config.keys():
            call_dictionary[option] = _create_object(
                section_config[option],
                init_signature.parameters[option].annotation, config)
        else:
            if option != "self" and init_signature.parameters[
                    option].default is Parameter.empty:
                raise ConfigurationError(
                    "Missing required argument '{0}' to create object '{1}' as specified in section '{2}'."
                    .format(option, class_name, section))

    for option, value in section_config.items():
        if option not in call_dictionary.keys():
            raise ConfigurationError(
                "Tried to initialize object '{0}' with argument '{1}' in section '{2}', "
                "which is not a possible argument of this class.".format(
                    class_name, option, section))

    if class_name != section:
        # If 'section' is an alias, we want instance.__class__.__name__ to be 'section (class_name)'
        # We use type to dynamically create a class inheriting from class_object with the proper name
        # Dict (last argument) is empty since we do not want to add attributes/methods
        # For dumping with dill to work, we have to set the __module__ to __main__
        # (see https://stackoverflow.com/questions/51016272)
        class_object = type("{0} ({1})".format(section, class_name),
                            (class_object, ), {"__module__": "__main__"})
    instance = class_object(**call_dictionary)
    return instance