def _get_sub( self, key: str, ensure_sub_existance: bool = True) -> Nullable[Dict[Any, Any]]: ''' Gets the /specified/ sub-context dictionary. If `ensure_sub_existance`, make sure it exists before returning it (create it with our bare entry if needed). Can return Null. ''' full_ctx = None sub_ctx = None if ensure_sub_existance: sub_ctx = self._ensure(key) full_ctx = self._get(False) else: full_ctx = self._get(False) if not full_ctx: return Null() key = key or self._key sub_ctx = self.data.get(key, Null()) return sub_ctx
def config(klass: Type['config'], caller_name: str, caller_dotted: label.DotStr, caller_context: Optional['VerediContext'], raises_error: Optional[bool] = True, ) -> Nullable['Configuration']: ''' Checks for a CONFIG link in config's spot in this context. Returns it if it exists. If it doesn't exist use `raises_error` to either: - Throw an error via this class's `exception()`. - Just return Null(). ''' # Get it. ctx = klass._get() cfg_obj = ctx.get(Link.CONFIG, Null()) # Error on it or return it? if not cfg_obj: if raises_error: msg = ("No Configuration present in the background " "context; cannot get config setting for {} ({}) " "without it.") raise klass.exception( caller_context, msg, caller_name, caller_dotted) else: # Esure it's null as opposed to Null, None, or Falsy. cfg_obj = Null() return cfg_obj
def per_test_set_up(self): self.start_engine_and_events() entity = self.create_entity() # import veredi.zest.debug.background # veredi.zest.debug.background.to_log('zest_input_to_output') # Make our request event. request = self.data_request(entity.id, PF2Rank.Phylum.MONSTER, 'Dragon', 'Aluminum Dragon') # print("per_test_set_up") # from veredi.zest.debug import background # background.to_log("unit-testing") # Trigger load with our request event. # Ask for our aluminum_dragon to be loaded. Expect 1 event for # each component in its data file. expected_events = len(self.expected_components) with log.LoggingManager.on_or_off(self.debugging): self.trigger_events(request, expected_events=expected_events) self.assertTrue(self.events) self.assertEqual(len(self.events), expected_events) # And we have DataLoadedEvents! Check 'em all; save the health one # for more checks. loaded_types = set() for loaded_event in self.events: self.assertIsInstance(loaded_event, DataLoadedEvent) # Did it make a thing? self.assertNotEqual(loaded_event.component_id, ComponentId.INVALID) # Get the thing and check it. component = self.manager.component.get(loaded_event.component_id) self.assertIsNotNone(component) self.assertEqual(loaded_event.component_id, component.id) self.assertIsInstance(component, DataComponent) comp_type = type(component) loaded_types.add(comp_type) # Make sure component and entity are enabled... self.force_alive(entity, component) # Not on entity because we don't have anyone hanging on them where # they belong yet... self.assertIs(entity.get(comp_type), Null()) # Now stuff it in there. self.manager.entity.attach(entity.id, component) ent_comp = entity.get(comp_type) self.assertIsNot(ent_comp, Null()) self.assertIs(ent_comp, component) # Now we have a set of all types - check against our expectations. self.assertEqual(loaded_types, self.expected_components) self.clear_events() return entity
def _get(klass: Type['registry'], key: str) -> Nullable[ContextMutableMap]: ''' Get registry's full sub-context from background context. ''' global _REGISTRY registry_entry = veredi.get().get(_REGISTRY, Null()) sub_entry = registry_entry.get(key, Null()) return sub_entry
def _get(klass: Type['ConfigRegistration'], path: 'ConfigRegistration', entry: Dict[str, Any]) -> Union[str, re.Pattern]: ''' Get value at end of `path` keys in `entry`. ''' if not path or not path.value: return Null() for node in path.value: entry = entry.get(node, Null()) return entry
def _define_vars(self) -> None: ''' Instance variable definitions, type hinting, doc strings, etc. ''' # Initialize our fields with Null... self._record_storage: threads.Local = threads.Local(context=Null(), group=Null(), success=Null(), exception=Null(), stack=Null()) '''
def exception(self, clear: Optional[bool] = False, exception: NullNoneOr[Exception] = None, stack: Optional[str] = None) -> None: ''' Save exception data off for injecting into LogRecord on filtering. If `clear` is True, saves `Null()` instead. ''' if clear: self._record_storage.exception = Null() self._record_storage.stack = Null() else: self._record_storage.exception = exception self._record_storage.stack = stack
def testing(klass: Type['ConfigContext'], context: VerediContext) -> Nullable[bool]: ''' Returns the unit-testing link field or Null. ''' unit_testing = context._sub_get(klass.KEY, ConfigLink.UNIT_TESTING) return unit_testing or Null()
def subproc(klass: Type['ConfigContext'], context: VerediContext) -> Nullable['SubToProcComm']: ''' Returns a SubToProcComm or Null. ''' comms = context._sub_get(klass.KEY, ConfigLink.SUB_PROC) return comms or Null()
def _define_vars(self) -> None: ''' Instance variable definitions, type hinting, doc strings, etc. ''' super()._define_vars() self._seconds_per_round: Decimal = None ''' How long in seconds that a round takes. It is definition data. ''' self._current_round: int = -1 ''' The number of the current round. Multiply by `self._seconds_per_round` to get `self._current_seconds`. It is saved data. ''' self._turn_order: Nullable[List[EntityId]] = Null() ''' The current round's turn order for entities. ''' self._turn_index: int = 0 '''
def set_turn_order(self, turn_order: NullNoneOr[Iterable[EntityId]], preserve_turn: bool = True) -> None: ''' Setter for the turn order list. If `preserve_turn` is True, this will try to keep the current turn on whatever entity it was on before the set. - This can fail and raise a ValueError if entity no longer exists in the list. If `preserve_turn` is False, this resets the turn order so that it is now the turn of the first entity in the new list. ''' # First, if we're in a turn_order, and being set to a new one (as # opposed to it being deleted): check to see if we can/should # `preserve_turn`. new_index = 0 if preserve_turn and self._turn_order and turn_order: curr_ent = self.turn if (curr_ent != EntityId.INVALID and curr_ent in turn_order): # Save their spot in the new list. new_index = turn_order.index(curr_ent) self._turn_order = turn_order or Null() self._turn_index = new_index
def get(self, *path: label.LabelInput) -> Nullable[Any]: ''' Regularizes `path` to a dotted list: Returns value in main document under (converted) `path`. - Returns Null() if `path` does not exist. ''' path = label.regularize(path) # Find out if there's anything at the end of the path. place = self._main() for key in path: # Check so we don't raise KeyError. if key not in place: return Null() # Update our place in the path and continue on to the next key. place = place[key] # We got here, so the path is either only a partial path, or we found a # leaf. We don't check for the difference right now, only return what # we ended up with. # # TODO: Figure out a way to return Null() if we're only a partial path? # - Does DataDict have recursive DataDicts for sub-entries or # something? return place
def test_command_execution(self): self.full_cmd_setup() cmd_str = 'unit-test 2 + 3' # Now ask for this to be executed. Should be a clean/valid input, # should have command name, should not have command prefix. status = self.commander.execute( None, cmd_str, self.user_input_ctx(cmd_str, Null(), None)) # In normal usage, we may need EventManager to publish() once or thrice # (or not), depending on the command. Here we're unit testing so we # don't care about all that and our command is done now. # # It rudely did everything during execution/invocation instead of # holding back any heavy-weight lifting for its turn in the update # tick... probably. I may have walked back this assumption since then. # These are unit tests and tend to get ignored until they fail... # # So, hi. Sorry this is failing for you. :( self.assertTrue(self.cmd_was_triggered) # We get status back immediately. Should be a success. self.assertIsInstance(status, CommandStatus) self.assertTrue(status.success) self.assertEqual(status.command_safe, cmd_str)
def parsers(klass: Type['input']) -> Nullable['Parcel']: ''' Checks for a CONFIG link in config's spot in this context. ''' ctx = klass._get() retval = ctx.get(Link.PARSERS, Null()) return retval
def _ability(self, name: str) -> Dict[str, Any]: ''' Get `name`'s entry in our persistent data or null. ''' entry = self.persistent.get(name, Null()) log.debug("ABILITY: {} entry for {}: {}", self.klass, name, entry) return entry
def _sub_get( self, ctx_key: str, field: Union[str, enum.Enum], default: NullNoneOr[Any] = Null() ) -> Nullable[Any]: ''' Gets `ctx_key` sub-context, then gets and returns value of `field`. Returns Null if ctx_key or field does not exist in this context, unless a `default` is provided. ''' default = default or Null() full_ctx = self._get(False) sub_ctx = full_ctx.get(ctx_key, Null()) return sub_ctx.get(field, Null()) or default
def get(self, id_or_type: CompIdOrType, allow_disabled: bool = False) -> Nullable[Component]: ''' Gets a component from this entity by ComponentId or ComponentType. Will return the component instance or Null(). ''' # Try to get by type first... component = self._components.get(id_or_type, None) log.debug("Get component '{}' by type: {}", id_or_type, component) if component: return self.comp_or_null(component, allow_disabled) # Ok... id maybe? component = self._component_manager.get(id_or_type) if component: # Make sure it's ours... # TODO [2020-06-19]: compare by entity_id instead of getting again # and comparing instance id? my_comp = self._components.get(type(component), None) if my_comp and component == my_comp: # If we don't want disabled, and this one isn't enabled # (and is therefore disabled), there isn't one for you to # have. return self.comp_or_null(component, allow_disabled) # Fall thorough - ain't got that one. return Null()
def _update_mitosis(self) -> VerediHealth: ''' Generic tick function. We do the same thing every tick state we process so do it all here. ''' # Already did our broadcast - nothing more to do. if self._registration_broadcast: log.debug("CommandRegistrationBroadcast: Did our thing already.") return self._health_check(SystemTick.MITOSIS) # Doctor checkup. if not self._healthy(SystemTick.MITOSIS): self._health_meter_update = self._health_log( self._health_meter_update, log.Level.WARNING, "HEALTH({}): Skipping ticks - our system health " "isn't good enough to process.", self.health) return self._health_check(SystemTick.MITOSIS) reg_broadcast = self._commander.registration(self.id, Null()) log.debug("CommandRegistrationBroadcast about to broadcast: {}", reg_broadcast) # TODO [2020-06-27]: better place to register these? veredi.zest.debug.registration.register(reg_broadcast) # All we want to do is send out the command registration broadcast. # Then we want to not tick this again. self._event_notify(reg_broadcast) self._registration_broadcast = True # Did a thing this tick so say we're PENDING... return VerediHealth.PENDING
def path(klass: Type['config']) -> Nullable[pathlib.Path]: ''' Checks for a PATH link in config's spot in this context. ''' ctx = klass._get() retval = ctx.get(Link.PATH, Null()) return retval
def meeting(klass: Type['manager']) -> Nullable['Meeting']: ''' Returns the Meeting of managers. Meeting has helper functions and maybe you want to access several managers from it? ''' ctx = klass._get() retval = ctx.get(Link.MEETING, Null()) return retval
def _get(self, data_type: DataType, *keychain: str) -> Any: ''' Gets a value from the definition or saved data, depending on `data_type`. Follows `keychain` in order to find the value. Example data_type: DataType.DEFINITION keychain: 'time', 'round' definition: --- !definition.game time: tick: veredi.game.time.tick.round round: 6 seconds -> 6 seconds - TODO: Type? Probably a timespan? ''' # --- # Sanity # --- # TODO: Verify keychain is correct for our document. Use a less # temporary solution than what Config does. # Config's "temporary" solution: # hierarchy = Document.hierarchy(doc_type) # if not hierarchy.valid(*keychain): # raise self._log_exception( # exceptions.ConfigError, # "Invalid keychain '{}' for {} document type. See its " # "Hierarchy class for proper layout.", # keychain, doc_type) # --- # Get the relevant record. # --- # Raises error if no data. data = self._data(data_type) # --- # Get the value by following the keychain. # --- value = data for key in keychain: value = value.get(key, None) if value is None: self._log_debug( "No data for key {key} in keychain {keychain} " "in our {data_type} data. " "'{key}' data: {}, " "all '{data_type}' data: {}", value, data, key=key, keychain=keychain, data_type=data_type) return Null() return value
def get(self, cmd_or_alias: str) -> Tuple[Nullable[Command], Nullable[str]]: ''' Get the command for `cmd_or_alias` string. Checks alias first so it can translate to command name and then only check commands once. Returns a tuple of: - command or Null - alias replacement, if `cmd_or_alias` is an alias or Null if not. ''' alias = self._aliases.get(cmd_or_alias, Null()) if alias: name, _ = sanitize.command_split(alias) else: name = cmd_or_alias return self._commands.get(name, Null()), alias
def _define_vars(self): ''' Instance variable definitions, type hinting, doc strings, etc. ''' self._debug: NullFalseOr[DebugFlag] = Null() ''' Debug flags for managers. ''' self._time_manager: NullFalseOr[TimeManager] = Null() ''' "Singleton" for TimeManager. - `False` indicates it explicitly does not exist. - `Null` indates it should but does not exist. ''' self._event_manager: NullFalseOr[EventManager] = Null() ''' "Singleton" for EventManager. - `False` indicates it explicitly does not exist. - `Null` indates it should but does not exist. ''' self._component_manager: NullFalseOr[ComponentManager] = Null() ''' "Singleton" for ComponentManager. - `False` indicates it explicitly does not exist. - `Null` indates it should but does not exist. ''' self._entity_manager: NullFalseOr[EntityManager] = Null() ''' "Singleton" for EntityManager. - `False` indicates it explicitly does not exist. - `Null` indates it should but does not exist. ''' self._system_manager: NullFalseOr[SystemManager] = Null() ''' "Singleton" for SystemManager. - `False` indicates it explicitly does not exist. - `Null` indates it should but does not exist. ''' self._data_manager: NullFalseOr[DataManager] = Null() ''' "Singleton" for DataManager. - `False` indicates it explicitly does not exist. - `Null` indates it should but does not exist. ''' self._identity_manager: NullFalseOr[IdentityManager] = Null() '''
def get(self, component_id: ComponentId) -> CompOrNull: ''' USES Null PATTERN!!! Get component from the component pool and return it. Component's LifeCycle is not checked so it might not be alive yet/anymore. Returns the Component object or the Null() singleton object. ''' return self._component_by_id.get(component_id, Null())
def get(klass: Type['veredi']) -> Nullable[ContextMutableMap]: ''' Get Veredi's root of the background context. Anything else in the background context is assumed to be other's (e.g. plugin's?). ''' global _CONTEXT, _ROOT if _CONTEXT is None: from copy import deepcopy _CONTEXT = deepcopy(_CONTEXT_LAYOUT) return _CONTEXT.get(_ROOT, Null())
def per_test_set_up(self): self.set_up_events() entity = self.create_entity() # Make our request event. request = self.data_request(entity.id, PF2Rank.Phylum.NPC, 'Townville', 'Skill Guy') self.assertFalse(self.events) # Ask for our Skill Guy data to be loaded. self.trigger_events(request) self.assertTrue(self.events) self.assertEqual(len(self.events), 1) # And we have a DataLoadedEvent! loaded_event = self.events[0] self.assertIsInstance(loaded_event, DataLoadedEvent) # Did it make a thing? self.assertNotEqual(loaded_event.component_id, ComponentId.INVALID) # Get the thing and check it. component = self.manager.component.get(loaded_event.component_id) self.assertIsNot(component, Null()) self.assertEqual(loaded_event.component_id, component.id) self.assertIsInstance(component, SkillComponent) # Make sure component and entity are enabled... self.force_alive(entity, component) # Not on entity because we don't have anyone hanging on them where they # belong yet... self.assertIs(entity.get(SkillComponent), Null()) # Now stuff it in there. self.manager.entity.attach(entity.id, component) ent_comp = entity.get(SkillComponent) self.assertIsNot(ent_comp, Null()) self.assertIs(ent_comp, component) self.clear_events() return entity
def __init__(self, debug_flags: NullNoneOr[DebugFlag]) -> None: self._define_vars() self._debug = debug_flags or Null() # --- # Logger! # --- # Set up ASAP so that we have self._log_*() working... ASAP? Yeah. self._log_config(self.dotted)
def sub_get( self, field: Union[str, enum.Enum], default: NullNoneOr[Any] = Null() ) -> Nullable[Any]: ''' Gets this context's sub-context, then gets and returns value of `field`. Returns Null if sub-context or field does not exist in this context, unless a `default` is provided. ''' return self._sub_get(self._key, field, default)
def comp_or_null(self, component: Component, allow_disabled: bool = False) -> Nullable[Component]: ''' Obeys or ignores `Component.enabled` based on `allow_disabled`. Returns component or None. ''' if not component.enabled: # Only return disabled component if allowed to. return component if allow_disabled else Null() return component
def context(self, clear: Optional[bool] = False, context: NullNoneOr['VerediContext'] = None) -> None: ''' Save context off for injecting into LogRecord on filtering. If `clear` is True, saves `Null()` instead. ''' if clear is True: context = Null() self._record_storage.context = context