Exemplo n.º 1
0
    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}.')
Exemplo n.º 2
0
    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.')
Exemplo n.º 3
0
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
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
    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}.')
Exemplo n.º 6
0
    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.")
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
    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.")