def _restore_state(self) -> None: from efro.util import enum_by_value try: sel: Optional[ba.Widget] sel_name = ba.app.ui.window_states.get(self.__class__.__name__, {}).get('sel_name') assert isinstance(sel_name, (str, type(None))) try: current_tab = enum_by_value(self.TabID, ba.app.config.get('Watch Tab')) except ValueError: current_tab = self.TabID.MY_REPLAYS self._set_tab(current_tab) if sel_name == 'Back': sel = self._back_button elif sel_name == 'TabContainer': sel = self._tab_container elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): try: sel_tab_id = enum_by_value(self.TabID, sel_name.split(':')[-1]) except ValueError: sel_tab_id = self.TabID.MY_REPLAYS sel = self._tab_row.tabs[sel_tab_id].button else: if self._tab_container is not None: sel = self._tab_container else: sel = self._tab_row.tabs[current_tab].button ba.containerwidget(edit=self._root_widget, selected_child=sel) except Exception: ba.print_exception(f'Error restoring state for {self}.')
def _restore_state(self) -> None: from efro.util import enum_by_value try: for tab in self._tabs.values(): tab.restore_state() sel: Optional[ba.Widget] winstate = ba.app.ui.window_states.get(type(self), {}) sel_name = winstate.get('sel_name', None) assert isinstance(sel_name, (str, type(None))) current_tab = self.TabID.ABOUT gather_tab_val = ba.app.config.get('Gather Tab') try: stored_tab = enum_by_value(self.TabID, gather_tab_val) if stored_tab in self._tab_row.tabs: current_tab = stored_tab except ValueError: pass self._set_tab(current_tab) if sel_name == 'Back': sel = self._back_button elif sel_name == 'TabContainer': sel = self._tab_container elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): try: sel_tab_id = enum_by_value(self.TabID, sel_name.split(':')[-1]) except ValueError: sel_tab_id = self.TabID.ABOUT sel = self._tab_row.tabs[sel_tab_id].button else: sel = self._tab_row.tabs[current_tab].button ba.containerwidget(edit=self._root_widget, selected_child=sel) except Exception: ba.print_exception('Error restoring gather-win state.')
def dict_key_from_raw(key: Any, keytype: Type) -> Any: """Given internal key, filter to world visible type.""" if issubclass(keytype, Enum): # We store all enum keys as strings; if the enum uses # int keys, convert back. for enumval in keytype: if isinstance(enumval.value, int): return enum_by_value(keytype, int(key)) break return enum_by_value(keytype, key) return key
def filter_input(self, data: Any, error: bool) -> dict: if not isinstance(data, dict): if error: raise TypeError('dict value expected') logging.error('Ignoring non-dict data for %s: %s', self, data) data = {} data_out = {} for key, val in data.items(): # For enum keys, make sure its a valid enum. if issubclass(self.d_keytype, Enum): try: _enumval = enum_by_value(self.d_keytype, key) except Exception as exc: if error: raise ValueError(f'No enum of type {self.d_keytype}' f' exists with value {key}') from exc logging.error('Ignoring invalid key type for %s: %s', self, data) continue # For all other keys we can check for exact types. elif not isinstance(key, self.d_keytype): if error: raise TypeError( f'Invalid key type; expected {self.d_keytype},' f' got {type(key)}.') logging.error('Ignoring invalid key type for %s: %s', self, data) continue data_out[key] = self.d_value.filter_input(val, error=error) return data_out
def _restore_state(self) -> None: from efro.util import enum_by_value try: sel: Optional[ba.Widget] sel_name = ba.app.ui.window_states.get(type(self), {}).get('sel_name') assert isinstance(sel_name, (str, type(None))) try: current_tab = enum_by_value(self.TabID, ba.app.config.get('Store Tab')) except ValueError: current_tab = self.TabID.CHARACTERS if self._show_tab is not None: current_tab = self._show_tab if sel_name == 'GetTickets' and self._get_tickets_button: sel = self._get_tickets_button elif sel_name == 'Back': sel = self._back_button elif sel_name == 'Scroll': sel = self._scrollwidget elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): try: sel_tab_id = enum_by_value(self.TabID, sel_name.split(':')[-1]) except ValueError: sel_tab_id = self.TabID.CHARACTERS sel = self._tab_row.tabs[sel_tab_id].button else: sel = self._tab_row.tabs[current_tab].button # If we were requested to show a tab, select it too.. if (self._show_tab is not None and self._show_tab in self._tab_row.tabs): sel = self._tab_row.tabs[self._show_tab].button self._set_tab(current_tab) if sel is not None: ba.containerwidget(edit=self._root_widget, selected_child=sel) except Exception: ba.print_exception(f'Error restoring state for {self}.')
def _value_from_input(self, cls: Type, fieldpath: str, typestr: str, value: Any) -> Any: """Convert an assigned value to what a dataclass field expects.""" # pylint: disable=too-many-return-statements simpletype = SIMPLE_NAMES_TO_TYPES.get(typestr) if simpletype is not None: if type(value) is not simpletype: # Special case: if they want to coerce ints to floats, do so. if (self._coerce_to_float and simpletype is float and type(value) is int): return float(value) _raise_type_error(fieldpath, type(value), (simpletype, )) return value if typestr.startswith('List[') and typestr.endswith(']'): return self._sequence_from_input(cls, fieldpath, typestr, value, 'List', list) if typestr.startswith('Set[') and typestr.endswith(']'): return self._sequence_from_input(cls, fieldpath, typestr, value, 'Set', set) if typestr.startswith('Optional[') and typestr.endswith(']'): subtypestr = typestr[9:-1] # Handle the 'None' case special and do the default # thing otherwise. if value is None: return None return self._value_from_input(cls, fieldpath, subtypestr, value) # Ok, its not a builtin type. It might be an enum or nested dataclass. cls2 = getattr(inspect.getmodule(cls), typestr, None) if cls2 is None: raise RuntimeError(f"Unable to resolve '{typestr}'" f" used by class '{cls.__name__}';" f' make sure all nested types are declared' f' in the global namespace of the module where' f" '{cls.__name__} is defined.") if dataclasses.is_dataclass(cls2): return self._dataclass_from_input(cls2, fieldpath, value) if issubclass(cls2, Enum): return enum_by_value(cls2, value) raise TypeError( f"Field '{fieldpath}' of type '{typestr}' is unsupported here.")
def _dict_from_input(self, cls: Type, fieldpath: str, anntype: Any, value: Any) -> Any: # pylint: disable=too-many-branches if not isinstance(value, dict): raise TypeError(f'Expected a dict for \'{fieldpath}\' on {cls};' f' got a {type(value)}.') childtypes = typing.get_args(anntype) assert len(childtypes) in (0, 2) out: Dict # We treat 'Any' dicts simply as json; we don't do any translating. if not childtypes or childtypes[0] is typing.Any: if not isinstance(value, dict) or not _is_valid_json(value): raise TypeError(f'Got invalid value for Dict[Any, Any]' f' at \'{fieldpath}\' on {cls};' f' all keys and values must be' f' json-compatible.') out = value else: out = {} keyanntype, valanntype = childtypes # Ok; we've got definite key/value types (which we verified as # valid during prep). Run all keys/values through it. # str keys we just take directly since that's supported by json. if keyanntype is str: for key, val in value.items(): if not isinstance(key, str): raise TypeError( f'Got invalid key type {type(key)} for' f' dict key at \'{fieldpath}\' on {cls};' f' expected a str.') out[key] = self._value_from_input(cls, fieldpath, valanntype, val) # int keys are stored in json as str versions of themselves. elif keyanntype is int: for key, val in value.items(): if not isinstance(key, str): raise TypeError( f'Got invalid key type {type(key)} for' f' dict key at \'{fieldpath}\' on {cls};' f' expected a str.') try: keyint = int(key) except ValueError as exc: raise TypeError( f'Got invalid key value {key} for' f' dict key at \'{fieldpath}\' on {cls};' f' expected an int in string form.') from exc out[keyint] = self._value_from_input( cls, fieldpath, valanntype, val) elif issubclass(keyanntype, Enum): # In prep we verified that all these enums' values have # the same type, so we can just look at the first to see if # this is a string enum or an int enum. enumvaltype = type(next(iter(keyanntype)).value) assert enumvaltype in (int, str) if enumvaltype is str: for key, val in value.items(): try: enumval = enum_by_value(keyanntype, key) except ValueError as exc: raise ValueError( f'Got invalid key value {key} for' f' dict key at \'{fieldpath}\' on {cls};' f' expected a value corresponding to' f' a {keyanntype}.') from exc out[enumval] = self._value_from_input( cls, fieldpath, valanntype, val) else: for key, val in value.items(): try: enumval = enum_by_value(keyanntype, int(key)) except (ValueError, TypeError) as exc: raise ValueError( f'Got invalid key value {key} for' f' dict key at \'{fieldpath}\' on {cls};' f' expected {keyanntype} value (though' f' in string form).') from exc out[enumval] = self._value_from_input( cls, fieldpath, valanntype, val) else: raise RuntimeError(f'Unhandled dict in-key-type {keyanntype}') return out
def _value_from_input(self, cls: Type, fieldpath: str, anntype: Any, value: Any) -> Any: """Convert an assigned value to what a dataclass field expects.""" # pylint: disable=too-many-return-statements # pylint: disable=too-many-branches origin = _get_origin(anntype) if origin is typing.Any: if not _is_valid_json(value): raise TypeError(f'Invalid value type for \'{fieldpath}\';' f' \'Any\' typed values must be types directly' f' supported by json; got' f' \'{type(value).__name__}\'.') return value if origin is typing.Union: # Currently the only unions we support are None/Value # (translated from Optional), which we verified on prep. # So let's treat this as a simple optional case. if value is None: return None childanntypes_l = [ c for c in typing.get_args(anntype) if c is not type(None) ] assert len(childanntypes_l) == 1 return self._value_from_input(cls, fieldpath, childanntypes_l[0], value) # Everything below this point assumes the annotation type resolves # to a concrete type. (This should have been verified at prep time). assert isinstance(origin, type) if origin in SIMPLE_TYPES: if type(value) is not origin: # Special case: if they want to coerce ints to floats, do so. if (self._coerce_to_float and origin is float and type(value) is int): return float(value) _raise_type_error(fieldpath, type(value), (origin, )) return value if origin in {list, set}: return self._sequence_from_input(cls, fieldpath, anntype, value, origin) if origin is tuple: return self._tuple_from_input(cls, fieldpath, anntype, value) if origin is dict: return self._dict_from_input(cls, fieldpath, anntype, value) if dataclasses.is_dataclass(origin): return self._dataclass_from_input(origin, fieldpath, value) if issubclass(origin, Enum): return enum_by_value(origin, value) if issubclass(origin, datetime.datetime): return self._datetime_from_input(cls, fieldpath, value) raise TypeError( f"Field '{fieldpath}' of type '{anntype}' is unsupported here.")