Exemple #1
0
def wrap(
    cls_or_instance: Union[EnumEncode, Type[EnumEncode]]
) -> Union['EnumWrap', Type['EnumWrap']]:
    '''
    Returns the EnumWrap class for this enum `klass`.

    Returns None if not found.
    '''
    # Get Class:
    klass, is_class = _to_class(cls_or_instance)

    # Do we know of this guy?
    wrap_type = _WRAPPED_ENUMS.get(klass, None)

    # Return what we were given: instance or class type
    if is_class:
        log.data_processing(
            label.normalize(_DOTTED, 'wrap'), "wrap:\n"
            "  --in--> {}\n"
            "  --type- {}\n"
            "  <-type- {}", cls_or_instance, wrap_type, wrap_type)
        return wrap_type

    wrap_instance = wrap_type(cls_or_instance)
    log.data_processing(
        label.normalize(_DOTTED, 'wrap'), "wrap:\n"
        "  -in--> {}\n"
        "  -type- {}\n"
        "  <-out- {}", cls_or_instance, wrap_type, wrap_instance)
    return wrap_instance
Exemple #2
0
def create(log_groups: List[log.Group],
           context: 'ConfigContext') -> EncodableRegistry:
    '''
    Create the EncodableRegistry instance.
    '''
    log.registration(label.normalize(_DOTTED, 'create'),
                     'Creating EncodableRegistry...')
    global codec
    codec = base_registrar(EncodableRegistry, log_groups, context, codec)
    log.registration(label.normalize(_DOTTED, 'create'),
                     'Created EncodableRegistry.')
Exemple #3
0
def register(cls_or_func: RegisterType,
             dotted: Optional[label.LabelInput] = None,
             unit_test_only: Optional[bool] = False) -> None:
    '''
    Register the `cls_or_func` with the `dotted` string to our registry.

    If `unit_test_only` is Truthy, the `cls_or_func` will be registered if we
    are running a unit test, or handed off to `ignore()` if we are not.
    '''
    log_dotted = label.normalize(_DOTTED, 'register')

    # ---
    # Sanity
    # ---
    if not dotted:
        # Check for class's dotted.
        try:
            dotted = cls_or_func.dotted
        except AttributeError:
            pass
        # No dotted string is an error.
        if not dotted:
            msg = ("Config sub-classes must either have a `dotted` "
                   "class attribute or be registered with a `dotted` "
                   "argument.")
            error = ValueError(msg, cls_or_func, dotted)
            log.registration(log_dotted, msg + "Got '{}' for {}.", dotted,
                             cls_or_func)
            raise log.exception(error, msg)

    # ---
    # Unit Testing?
    # ---
    # Unit-test registrees should continue on if in unit-testing mode,
    # or be diverted to ignore if not.
    if unit_test_only and not background.testing.get_unit_testing():
        ignore(cls_or_func)
        return

    # ---
    # Register
    # ---
    # Registry should check if it is ignored already by someone previous,
    # if it cares.
    dotted_str = label.normalize(dotted)
    log.registration(log_dotted, "{}: Registering '{}' to '{}'...",
                     config.klass, dotted_str, cls_or_func.__name__)

    dotted_args = label.regularize(dotted)
    config.register(cls_or_func, *dotted_args)

    log.registration(log_dotted, "{}: Registered '{}' to '{}'.", config.klass,
                     dotted_str, cls_or_func.__name__)
Exemple #4
0
def register_enum(
    klass: Type['Encodable'],
    dotted: Optional[label.LabelInput] = None,
    name_encode: Optional[str] = None,
    name_klass: Optional[str] = None,
    enum_encode_type: Optional['enum.EnumWrap'] = None,
    unit_test_only: Optional[bool] = False,
) -> None:
    '''
    Create a WrapEnum Encodable for this enum class.

    Required:
      - klass
      - dotted
      - name_encode
      - enum_encode_type

    Optional:
      - name_klass
      - unit_test_only
    '''
    if not enum.needs_wrapped(klass):
        log_dotted = label.normalize(_DOTTED, 'register_enum')
        msg = ("Only Enum sub-classes should be wrapped in an EnumWrap for "
               "Encodable functionality. Call `register()` "
               "instead of `register_wrap()` for this class.")
        error = TypeError(msg, klass, dotted)
        log.registration(log_dotted, msg + f" {klass}")
        raise log.exception(error,
                            msg,
                            data={
                                'klass': klass,
                                'dotted': label.normalize(dotted),
                                'name_encode': name_encode,
                                'name_klass': name_klass,
                                'enum_encode_type': enum_encode_type,
                                'unit_test_only': unit_test_only,
                            })

    # ------------------------------
    # Create wrapper and register it.
    # ------------------------------
    # This is an enum and we need to make a wrapper for it to be able to be
    # an Encodable.
    wrapped = enum.encodable(klass,
                             name_dotted=dotted,
                             name_string=name_encode,
                             name_klass=name_klass,
                             enum_encode_type=enum_encode_type)
    register(wrapped, dotted=dotted, unit_test_only=unit_test_only)
Exemple #5
0
def configuration(rules: label.LabelInput,
                  game_id: Any,
                  path: pathlib.Path = None) -> Configuration:
    '''
    Find config file and parse into the Configuration object for this game.

    `rules` is the Label for the rules type, e.g. 'veredi.rules.d20.pf2'

    `game_id` is the repository identity key for the specific game.

    `path` can be either a directory or the config file path. If `path` is
    None, this looks in the current directory for the first file that matches
    the _CONFIG_FILE_NAME_GLOBS patterns.
    '''
    log_dotted = label.normalize(_DOTTED, 'configuration')
    log.start_up(log_dotted, "Creating Veredi Configuration...")

    # ------------------------------
    # Sanity Checks?
    # ------------------------------
    log.start_up(log_dotted, "Checking Inputs...", path,
                 _CONFIG_FILE_NAME_GLOBS)

    # Normalize rules to a dotted string.
    rules = label.normalize(rules)

    # game_id... dunno much about it until we get a repository for it.

    # ------------------------------
    # Figure out actual config file path from input path.
    # ------------------------------
    log.start_up(log_dotted, "Finding Config File...", path,
                 _CONFIG_FILE_NAME_GLOBS)
    path = _config_path(log_dotted, path)

    # ------------------------------
    # Create Configuration.
    # ------------------------------
    log.start_up(log_dotted, "Creating Configuration...", path,
                 _CONFIG_FILE_NAME_GLOBS)
    config = Configuration(rules, game_id, config_path=path)

    # ------------------------------
    # Done.
    # ------------------------------
    log.start_up(log_dotted,
                 "Done creating Veredi Configuration.",
                 log_success=log.SuccessType.SUCCESS)
    return config
Exemple #6
0
def unwrap(instance: Union['EnumWrap', 'Encodable']) -> EnumEncode:
    '''
    If `instance` is an EnumWrap, returns the enum instance/value that the
    EnumWrap `instance` contains. Else returns `instance`.
    '''
    if not isinstance(instance, EnumWrap):
        log.data_processing(label.normalize(_DOTTED, 'unwrap'), "unwrap:\n"
                            "  --ignore-> {}\n"
                            "  <-ignore-- {}", instance, instance)
        return instance

    log.data_processing(label.normalize(_DOTTED, 'unwrap'), "unwrap:\n"
                        "  -in--> {}\n"
                        "  <-out- {}", instance, instance.enum)
    return instance.enum
Exemple #7
0
    def __set__(self, instance: Optional[Any],
                dotted: Optional[label.LabelInput]) -> None:
        '''
        Setter for context's dotted label.
        '''
        if not instance:
            return

        # Walk into context dict using keys to find dotted.
        # Don't go to final key. We'll use that to set it.
        data, keys = self._get_from_ctx(instance)
        for key in keys[:-1]:
            # What to do with an empty key in data?.. Not exactly expected but
            # I guess set to an empty dict.
            data = data.setdefault(key, {})
            if not data:
                return

        # Found the dict sub-entry that will hold our dotted label.
        if not dotted:
            # It might have a dotted already. Try to clear it out.
            data.pop(keys[-1], None)
        else:
            # Normalize and set the dotted string now.
            data[keys[-1]] = label.normalize(dotted)
Exemple #8
0
    def __init__(self,
                 test_case: 'TestCase',
                 test_name: Optional[str] = None,
                 data: MutableMapping[str, Any] = {},
                 starting_context: MutableMapping[str, Any] = None) -> None:
        '''
        Initialize Context with test class/name. e.g.:

        context = UnitTestContext(self,
                                  'test_something',
                                  data={...})
        '''
        # Build dotted based on inputs.
        dotted = (test_case.dotted if not test_name else label.normalize(
            test_case.dotted, test_name))
        super().__init__(dotted, 'unit-testing')

        # Set starting context.
        if starting_context:
            self.data = starting_context
        else:
            self.data = {}

        self.dotted = test_case.dotted

        # Ensure our sub-context and ingest the provided data.
        sub = self._ensure()
        # Munge any starting_context/defaults with what was specifically
        # supplied as the subcontext data.
        self._merge_dicts(data, sub, Conflict.RECEIVER_MUNGED,
                          'UnitTestContext.init.pull', 'from')
Exemple #9
0
    def _make(self, *name: str) -> str:
        '''
        Make **ANY** LogName from `*name` strings.

        Should use `rooted()` unless you're special.
        '''
        return label.normalize(*name)
Exemple #10
0
 def __str__(self) -> str:
     '''
     Python 'to string' function.
     '''
     # Build a string based on whatever taxonomic ranks we have...
     dotted = label.normalize(*[str(rank) for rank in self._taxon])
     return f"{self.__class__.__name__}['{dotted}']"
Exemple #11
0
def _tear_down_end(proc: ProcToSubComm,
                   logger: Optional[log.PyLogType]) -> ExitCodeTuple:
    '''
    Checks that process finished shutdown. If not, we terminate it immediately.

    In any case, we return its exit code.
    '''
    _log_dotted = label.normalize(_DOTTED_FUNCS, '_tear_down.end')

    # Make sure it shut down and gave a good exit code.
    if (proc.process and proc.process.is_alive()
            and proc.process.exitcode is None):
        # Still not exited; terminate it.
        log.group_multi(_LOG_KILL,
                        _log_dotted, "_tear_down_end({}): "
                        "'{}' has still not exited; terminate it... "
                        "Immediately.",
                        proc.name,
                        proc.name,
                        veredi_logger=logger)
        proc.process.terminate()

    exitcode = (proc.process.exitcode if (proc and proc.process) else None)
    log.group_multi(_LOG_KILL,
                    _log_dotted, "_tear_down_end({}): "
                    "'{}' has exited with exit code: {}",
                    proc.name,
                    proc.name,
                    str(exitcode),
                    veredi_logger=logger)
    return ExitCodeTuple(proc.name, exitcode)
Exemple #12
0
 def dotted(klass: Type['ConfigRegistration'],
            entry: Dict[str, Any]) -> label.DotStr:
     '''
     Returns the DOTTED entry of this registration entry.
     '''
     value = klass._get(klass.DOTTED, entry)
     return label.normalize(value)
Exemple #13
0
def _tear_down_check(
        proc: ProcToSubComm,
        logger: Optional[log.PyLogType]) -> Optional[ExitCodeTuple]:
    '''
    Checks that process exists, then if process has good exit code.
    '''
    _log_dotted = label.normalize(_DOTTED_FUNCS, '_tear_down.check')

    if not proc or not proc.process:
        if proc:
            log.group_multi(_LOG_KILL,
                            _log_dotted, "_tear_down_check({}): "
                            "No {} to stop.",
                            proc.name,
                            proc.name,
                            veredi_logger=logger)
        else:
            log.group_multi(_LOG_KILL,
                            _log_dotted, "_tear_down_check(): "
                            "Cannot stop None/Null sub-process: {}",
                            proc,
                            veredi_logger=logger)
        # Pretend it exited with good exit code?
        return ExitCodeTuple(proc.name, 0)

    if proc.process.exitcode == 0:
        log.group_multi(_LOG_KILL,
                        _log_dotted, "_tear_down_check({}): "
                        "Process '{}' is already stopped.",
                        proc.name,
                        proc.name,
                        veredi_logger=logger)
        return ExitCodeTuple(proc.name, proc.process.exitcode)

    return None
Exemple #14
0
    def __init__(self,
                 context: Optional['VerediContext'],
                 cid:     ComponentId) -> None:
        '''DO NOT CALL THIS UNLESS YOUR NAME IS ComponentManager!'''
        super().__init__(context, cid)

        # Set up our actual mock dotted label.
        self.dotted = label.normalize(self.dotted,
                                      self.__class__.__name__.lower())
Exemple #15
0
    def __init__(self,
                 context:  Optional['VerediContext'],
                 sid:      SystemId,
                 managers: 'Meeting') -> None:
        super().__init__(context, sid, managers)

        # Set up our actual mock dotted label.
        self.dotted = label.normalize(self.dotted,
                                      self.klass.lower())
Exemple #16
0
def create(log_groups: List[log.Group],
           context: 'ConfigContext') -> ConfigRegistry:
    '''
    Create the ConfigRegistry instance.
    '''
    log_dotted = label.normalize(_DOTTED, 'create')
    log.registration(log_dotted, 'Creating EncodableRegistry...')
    global config
    config = base_registrar(ConfigRegistry, log_groups, context, config)
    log.registration(log_dotted, 'Created EncodableRegistry.')
Exemple #17
0
 def rooted(self, *name: str) -> str:
     '''
     Make a LogName rooted from LogName enum called from.
     Examples:
       LogName.ROOT.rooted('jeff')
         -> 'veredi.jeff'
       LogName.MULTIPROC.rooted('server', 'jeff')
         -> 'veredi.multiproc.server.jeff'
     '''
     return label.normalize(str(self), *name)
Exemple #18
0
def init(config: 'Configuration') -> None:
    '''
    Do some importing and set-up.
    '''
    log_dotted = label.normalize(_DOTTED, 'init')
    log.start_up(log_dotted, "Initializing Veredi...")

    registration(config)

    log.start_up(log_dotted, "Initialization done.")
Exemple #19
0
def nonblocking_tear_down_start(
        proc: ProcToSubComm) -> Optional[ExitCodeTuple]:
    '''
    Kicks off tear-down. Caller will have to loop calling
    `nonblocking_tear_down_wait` for however long they want to wait for a clean
    shutdown, then call `nonblocking_tear_down_end` to finish.
    '''
    _log_dotted = label.normalize(_DOTTED_FUNCS, 'tear_down.nonblocking.start')
    logger = log.get_logger(proc.name)

    log.group_multi(_LOG_KILL,
                    _log_dotted,
                    "nonblocking_tear_down_start({}): Begin.",
                    proc.name,
                    veredi_logger=logger)

    # ------------------------------
    # Sanity Check, Early Out.
    # ------------------------------
    result = _tear_down_check(proc, logger)
    if result:
        log.group_multi(_LOG_KILL,
                        _log_dotted,
                        "nonblocking_tear_down_start({}): ",
                        "Check returned exit code: {}",
                        proc.name,
                        result,
                        veredi_logger=logger)
        return result

    # ------------------------------
    # Kick off tear-down.
    # ------------------------------
    log.group_multi(_LOG_KILL,
                    _log_dotted,
                    "nonblocking_tear_down_start({}): ",
                    "Starting tear-down...",
                    proc.name,
                    veredi_logger=logger)
    _tear_down_start(proc, logger)
    # No return value for `_tear_down_start()`; can't check anything.
    # if result:
    #     log.group_multi(_LOG_KILL,
    #                     _log_dotted,
    #                     "nonblocking_tear_down_start({}): ",
    #                     "_tear_down_start returned exit code: {}",
    #                     proc.name, result,
    #                     veredi_logger=logger)
    #     return result

    log.group_multi(_LOG_KILL,
                    _log_dotted,
                    "nonblocking_tear_down_start({}): Done.",
                    proc.name,
                    veredi_logger=logger)
Exemple #20
0
 def _background(self):
     '''
     Get background data for init_background()/background.mediator.set().
     '''
     self._bg = {
         'dotted': self.dotted,
         'type': label.normalize('websocket', self.name),
         'serdes': self._serdes.dotted,
         'codec': self._codec.dotted,
     }
     return self._bg, background.Ownership.SHARE
Exemple #21
0
    def _query_split(self, component: Component, *entry: str) -> ValueMilieu:
        '''
        `entry` args must have been canonicalized.

        Gets `entry` from the component. Returns value and dotted
        entry string. E.g.:

          _query_split(component,
                       'strength',
                       'score')
            -> (20, 'strength.score')
        '''
        return ValueMilieu(component.query(*entry), label.normalize(*entry))
Exemple #22
0
    def rules(self, context: 'VerediContext') -> Nullable[RulesGame]:
        '''
        Creates and returns the proper RulesGame object for this specific game
        with its game definition and saved data.

        Raises a ConfigError if no rules label or game id.
        '''
        log_groups = [log.Group.START_UP, log.Group.DATA_PROCESSING]
        log.group_multi(
            log_groups, self.dotted, "rules: Creating game rules object "
            "from rules: {}, id: {}", self._rules, self._id)

        # ---
        # Sanity
        # ---
        if not self._rules or not self._id:
            log.group_multi(log_groups,
                            self.dotted,
                            "rules: Failed to create game rules... missing "
                            "our rules or id: rules {}, id: {}",
                            self._rules,
                            self._id,
                            log_success=False)
            raise log.exception(
                ConfigError,
                "No rules label or id for game; cannot create the "
                "RulesGame object. rules: {}, id: {}", self._rules, self._id)

        # ---
        # Context
        # ---
        # Allow something else if the caller wants, but...
        if not context:
            # ...this default w/ rules/id should be good in most cases.
            context = ConfigContext(self._path,
                                    self.dotted,
                                    key=self._rules,
                                    id=self._id)

        # ---
        # Create the rules.
        # ---
        rules = self.create_from_label(
            # '<rules-dotted>.game' is our full dotted string.
            label.normalize(self._rules, 'game'),
            context=context)
        log.group_multi(log_groups,
                        self.dotted,
                        "rules: Created game rules.",
                        log_success=True)
        return rules
Exemple #23
0
class LogName(enum.Enum):

    ROOT = label.normalize('veredi')
    '''
    The default/root veredi logger.
    '''

    MULTIPROC = label.normalize(ROOT, 'multiproc')
    '''
    multiproc's logger for setting up/tearing down sub-processes.
    '''

    # TODO [2020-09-12]: More logger names. Some formats?

    def _make(self, *name: str) -> str:
        '''
        Make **ANY** LogName from `*name` strings.

        Should use `rooted()` unless you're special.
        '''
        return label.normalize(*name)

    def rooted(self, *name: str) -> str:
        '''
        Make a LogName rooted from LogName enum called from.
        Examples:
          LogName.ROOT.rooted('jeff')
            -> 'veredi.jeff'
          LogName.MULTIPROC.rooted('server', 'jeff')
            -> 'veredi.multiproc.server.jeff'
        '''
        return label.normalize(str(self), *name)

    def __str__(self) -> str:
        '''
        Returns value string of enum.
        '''
        return self.value
Exemple #24
0
def ignore(ignoree: RegisterType) -> None:
    '''
    For flagging an Config class as one that is a base class
    or should not be registered for some other reason.
    '''
    log_dotted = label.normalize(ConfigRegistry.dotted, 'ignore')
    log.registration(log_dotted,
                     "{}: '{}' marking as ignored for registration...",
                     config.klass, ignoree)

    config.ignore(ignoree)

    log.registration(log_dotted, "{}: '{}' marked as ignored.", config.klass,
                     ignoree)
Exemple #25
0
def add(system: System) -> SystemId:
    '''
    Helper to take an already created system, and add it into the
    SystemManager's systems. Returns the SystemId assigned by SystemManager.
    '''
    log_dotted = label.normalize(_DOTTED, 'add')
    log.start_up(log_dotted, f"Adding system '{str(system.klass)}'...")

    sid = background.manager.system.add(system)

    log.start_up(log_dotted, f"Added system '{str(system.klass)}' with "
                 f"SystemId: {str(sid)}",
                 log_success=log.SuccessType.SUCCESS)
    return sid
Exemple #26
0
def ignore(ignoree: Type['Encodable']) -> None:
    '''
    For flagging an Encodable class as one that is a base class
    or should not be registered for some other reason.
    '''
    log_dotted = label.normalize(EncodableRegistry.dotted, 'ignore')
    log.registration(log_dotted,
                     "{}: '{}' marking as ignored for registration...",
                     codec.klass, ignoree)

    codec.ignore(ignoree)

    log.registration(log_dotted, "{}: '{}' marked as ignored.", codec.klass,
                     ignoree)
Exemple #27
0
    def get_by_dotted(self, dotted: label.LabelInput,
                      context: Optional[VerediContext]) -> 'RegisterType':
        '''
        Get by dotted name.

        Returns a registered class/func from the dot-separated keys (e.g.
        "repository.player.file-tree").

        Context just used for errors/exceptions.

        Raises:
          KeyError - dotted string not found in our registry.
        '''
        registration = self._registry
        split_keys = label.regularize(dotted)

        # ---
        # Walk into our registry using the keys for our path.
        # ---
        i = 0
        for key in split_keys:
            if registration is None:
                break
            # This can throw the KeyError...
            try:
                registration = registration[key]
            except KeyError as error:
                raise log.exception(
                    RegistryError,
                    "Registry has nothing at: {} (full path: {})",
                    split_keys[:i + 1],
                    split_keys,
                    context=context) from error
            i += 1

        # ---
        # Sanity Check - ended at leaf node?
        # ---
        if isinstance(registration, dict):
            raise log.exception(RegistryError,
                                "Registry for '{}' is not at a leaf - "
                                "still has entries to go: {}",
                                label.normalize(dotted),
                                registration,
                                context=context)

        # Good; return the leaf value (a RegisterType).
        return registration
Exemple #28
0
def _tear_down_start(
    proc: ProcToSubComm,
    logger: Optional[log.PyLogType],
) -> None:
    '''
    Set shutdown flag. Proc should notice soon (not immediately) and start its
    shutdown.
    '''
    _log_dotted = label.normalize(_DOTTED_FUNCS, '_tear_down.start')
    log.group_multi(_LOG_KILL,
                    _log_dotted, "_tear_down_start({}): "
                    "Asking '{}' to end gracefully...",
                    proc.name,
                    proc.name,
                    veredi_logger=logger)
    proc.shutdown.set()
Exemple #29
0
def create(config: Optional[Configuration],
           context: VerediContext,
           system_type: Type[System],
           debug_flags: Optional[DebugFlag] = None) -> SystemId:
    '''
    Helper to create a system. Returns the created system's SystemId.
    '''
    log_dotted = label.normalize(_DOTTED, 'create')
    log.start_up(log_dotted, f"Creating system '{str(system_type.klass)}'...")

    sid = background.manager.system.create(system_type, context)

    log.start_up(log_dotted, f"Created system '{str(system_type.klass)}' with "
                 f"SystemId: {str(sid)}",
                 log_success=log.SuccessType.SUCCESS)
    return sid
Exemple #30
0
def engine(
    configuration: Configuration,
    meeting: Meeting,
    # Optional Debug Stuff:
    debug_flags: Optional[DebugFlag] = None,
) -> Engine:
    '''
    Create and configure a game engine using the other supplied parameters.
    '''
    log_dotted = label.normalize(_DOTTED, 'engine')
    log.start_up(log_dotted, "Building the Engine...")

    # ---
    # Sanity.
    # ---
    if not configuration:
        msg = "Configuration must be provided."
        error = ConfigError(msg,
                            data={
                                'configuration': str(configuration),
                                'meeting': str(meeting),
                            })
        raise log.exception(error, msg)

    if not meeting:
        # This shouldn't happen - should raise an error before here, right?
        msg = "Managers' Meeting must be provided."
        error = ConfigError(msg,
                            data={
                                'configuration': str(configuration),
                                'meeting': str(meeting),
                            })
        raise log.exception(error, msg)

    # ---
    # Create engine.
    # ---
    log.start_up(log_dotted, "Initializing the Engine...")
    owner = None
    campaign_id = None
    engine = Engine(owner, campaign_id, configuration, meeting, debug_flags)

    log.start_up(log_dotted,
                 "Done building the Engine.",
                 log_success=log.SuccessType.SUCCESS)
    return engine