Ejemplo n.º 1
0
def get_dataclass_type_from_forward_ref(forward_ref: Type, Serializable=Serializable) -> Optional[Type]:
    arg = tpi.get_forward_arg(forward_ref)
    potential_classes: List[Type] = []

    for serializable_class in Serializable.subclasses:
        if serializable_class.__name__ == arg:
            potential_classes.append(serializable_class)

    if not potential_classes:
        logger.warning(
            f"Unable to find a corresponding type for forward ref "
            f"{forward_ref} inside the registered {Serializable} subclasses. "
            f"(Consider adding {Serializable} as a base class to <{arg}>? )."
        )
        return None
    elif len(potential_classes) > 1:
        logger.warning(
            f"More than one potential {Serializable} subclass was found for "
            f"forward ref '{forward_ref}'. The appropriate dataclass will be "
            f"selected based on the matching fields. \n"
            f"Potential classes: {potential_classes}"
        )
        return Serializable
    else:
        assert len(potential_classes) == 1
        return potential_classes[0]
Ejemplo n.º 2
0
    def _all_subclasses(typ, *, module=None):
        """
        Return all subclasses of a given type.

        The type must be one of

         - :class:`GTScriptAstNode` (returns all subclasses of the given class)
         - :class:`Union` (return the subclasses of the united)
         - :class:`ForwardRef` (resolve the reference given the specified module and return its subclasses)
         - built-in python type: :class:`str`, :class:`int`, `type(None)` (return as is)
        """
        if inspect.isclass(typ) and issubclass(typ,
                                               gtscript_ast.GTScriptASTNode):
            result = {
                typ,
                *typ.__subclasses__(),
                *[
                    s for c in typ.__subclasses__()
                    for s in PyToGTScript._all_subclasses(c)
                    if not inspect.isabstract(c)
                ],
            }
            return result
        elif inspect.isclass(typ) and typ in [
                gtc.common.AssignmentKind,
                gtc.common.UnaryOperator,
                gtc.common.BinaryOperator,
        ]:
            # note: other types in gtc.common, e.g. gtc.common.DataType are not valid leaf nodes here as they
            #  map to symbols in the gtscript ast and are resolved there
            assert issubclass(typ, enum.Enum)
            return {typ}
        elif typing_inspect.is_union_type(typ):
            return {
                sub_cls
                for el_cls in typing_inspect.get_args(typ)
                for sub_cls in PyToGTScript._all_subclasses(el_cls,
                                                            module=module)
            }
        elif isinstance(typ, typing.ForwardRef):
            type_name = typing_inspect.get_forward_arg(typ)
            if not hasattr(module, type_name):
                raise ValueError(
                    f"Reference to type `{type_name}` in `ForwardRef` not found in module {module.__name__}"
                )
            return PyToGTScript._all_subclasses(getattr(module, type_name),
                                                module=module)
        elif typ in [
                type_definitions.StrictStr,
                type_definitions.StrictInt,
                type_definitions.StrictFloat,
                str,
                int,
                float,
                type(None),
        ]:  # TODO(tehrengruber): enhance
            return {typ}

        raise ValueError(f"Invalid field type {typ}")
Ejemplo n.º 3
0
def get_dataclass_types_from_forward_ref(
        forward_ref: Type,
        serializable_base_class: Type[S] = SerializableMixin) -> List[Type[S]]:
    arg = tpi.get_forward_arg(forward_ref)
    potential_classes: List[Type] = []
    for serializable_class in serializable_base_class.subclasses:
        if serializable_class.__name__ == arg:
            potential_classes.append(serializable_class)
    return potential_classes
Ejemplo n.º 4
0
 def _resolve_forward_ref(self, t: Type[Any]) -> Type[Any]:
     if isinstance(t, str) or is_forward_ref(t):
         if isinstance(t, str):
             name = t
         else:
             name = get_forward_arg(t)
         if name in self.forward_ref_dict:
             return self.forward_ref_dict[name]
         else:
             raise Exception(f"Unknown forward reference '{name}'")
     else:
         return t
Ejemplo n.º 5
0
 def _get_type_name(t: Type) -> str:
     if hasattr(t, "__name__"):
         return t.__name__
     else:
         origin = get_origin(t)
         if origin:
             name = MappingException._get_type_name(origin)
         elif is_forward_ref(t):
             name = get_forward_arg(t)
         else:
             name = t._name
         args = list(map(lambda x: MappingException._get_type_name(x), get_args(t)))
         if len(args) != 0:
             return f"{name}[{', '.join(args)}]"
         else:
             return name
Ejemplo n.º 6
0
def get_parsing_fn(t: Type[T]) -> Callable[[Any], T]:
    """Gets a parsing function for the given type or type annotation.

    Args:
        t (Type[T]): A type or type annotation.

    Returns:
        Callable[[Any], T]: A function that will parse a value of the given type
            from the command-line when available, or a no-op function that
            will return the raw value, when a parsing fn cannot be found or
            constructed.
    """
    if t in _parsing_fns:
        logger.debug(f"The type {t} has a dedicated parsing function.")
        return _parsing_fns[t]

    elif t is Any:
        logger.debug(f"parsing an Any type: {t}")
        return no_op

    # TODO: Do we want to support parsing a Dict from command-line?
    # elif is_dict(t):
    #     logger.debug(f"parsing a Dict field: {t}")
    #     args = get_type_arguments(t)
    #     if len(args) != 2:
    #         args = (Any, Any)
    #     return parse_dict(*args)

    # TODO: This would require some sort of 'postprocessing' step to convert a
    # list to a Set or something like that.
    # elif is_set(t):
    #     logger.debug(f"parsing a Set field: {t}")
    #     args = get_type_arguments(t)
    #     if len(args) != 1:
    #         args = (Any,)
    #     return parse_set(args[0])

    elif is_tuple(t):
        logger.debug(f"parsing a Tuple field: {t}")
        args = get_type_arguments(t)
        if is_homogeneous_tuple_type(t):
            if not args:
                args = (str, ...)
            parsing_fn = get_parsing_fn(args[0])
        else:
            parsing_fn = parse_tuple(args)
            parsing_fn.__name__ = str(t)
        return parsing_fn

    elif is_list(t):
        logger.debug(f"parsing a List field: {t}")
        args = get_type_arguments(t)
        assert len(args) == 1
        return parse_list(args[0])

    elif is_union(t):
        logger.debug(f"parsing a Union field: {t}")
        args = get_type_arguments(t)
        return parse_union(*args)

    elif is_enum(t):
        logger.debug(f"Parsing an Enum field of type {t}")
        return parse_enum(t)
    # import typing_inspect as tpi
    # from .serializable import get_dataclass_type_from_forward_ref, Serializable

    if tpi.is_forward_ref(t):
        forward_arg = tpi.get_forward_arg(t)
        for t, fn in _parsing_fns.items():
            if getattr(t, "__name__", str(t)) == forward_arg:
                return fn

    if tpi.is_typevar(t):
        bound = tpi.get_bound(t)
        logger.debug(f"parsing a typevar: {t}, bound type is {bound}.")
        if bound is not None:
            return get_parsing_fn(bound)

    logger.debug(f"Couldn't find a parsing function for type {t}, will try "
                 f"to use the type directly.")
    return t
Ejemplo n.º 7
0
 def test_get_forward_arg(self):
     tp = List["FRef"]
     fr = get_args(tp)[0]
     self.assertEqual(get_forward_arg(fr), "FRef")
     self.assertEqual(get_forward_arg(tp), None)
Ejemplo n.º 8
0
def sanitize_if_forward_ref(subscripted_type: type) -> Union[type, str]:
    if is_forward_ref(subscripted_type):
        return get_forward_arg(subscripted_type)
    return subscripted_type