def dict_to_alfacase_schema(type_, block_indentation): key_type, value_type = ( typing_inspect.get_last_args(type_) if sys.version_info.minor == 6 else typing_inspect.get_args(type_) ) return f"MapPattern(Str(), {_get_attr_value(value_type)})"
def type_match(src_type, tgt_type): """ Compares two type hints to see if they overlap. This does not require exact matches if the target type type_match( """ match = False if tgt_type == Any: match = True elif not issubclass(tgt_type.__class__, typing.GenericMeta): # if the target type is not a generic template, see if we have a subclass match match = issubclass(src_type, tgt_type) else: # check classes are the same match = src_type.__class__ == tgt_type.__class__ if match: src_origin = typing_inspect.get_last_origin(src_type) tgt_origin = typing_inspect.get_last_origin(tgt_type) match = typing_inspect.get_origin(Union[src_origin, tgt_origin]) != Union if not match: return False src_args = typing_inspect.get_last_args(src_type) tgt_args = typing_inspect.get_last_args(tgt_type) if len(tgt_args) == 0: return True if len(src_args) != len(tgt_args): return False for src_arg, tgt_arg in zip(src_args, tgt_args): if not type_match(src_arg, tgt_arg): return False return True
def test_last_args(self): T = TypeVar('T') S = TypeVar('S') self.assertEqual(get_last_args(int), ()) self.assertEqual(get_last_args(Union), ()) self.assertEqual(get_last_args(ClassVar[int]), (int,)) self.assertEqual(get_last_args(Union[T, int]), (T, int)) self.assertEqual(get_last_args(Iterable[Tuple[T, S]][int, T]), (int, T)) self.assertEqual(get_last_args(Callable[[T, S], int]), (T, S, int)) self.assertEqual(get_last_args(Callable[[], int]), (int,))
def auto_any(cls) -> schema.Schema: if inspect.isclass(cls): if issubclass(cls, bool): return schema.BooleanSchema() elif issubclass(cls, float): return schema.NumberSchema() elif issubclass(cls, int): return schema.IntSchema() elif issubclass(cls, str): return schema.StringSchema() elif issubclass(cls, datetime): return schema.StringSchema(format='date-time') elif issubclass(cls, date): return schema.StringSchema(format='date') elif issubclass(cls, timedelta): return schema.NumberSchema(format='double') elif issubclass(cls, List): vt, = get_args(cls) subschema = auto_any(vt) return schema.ArraySchema(items=subschema) elif issubclass(cls, Dict): kt, vt = get_args(cls) if not issubclass(kt, str): raise NotImplementedError(('class3', cls, kt)) subschema = auto_any(vt) return ObjectSchema(additional=subschema, ) elif is_dataclass(cls): return auto_dataclass(cls) else: raise NotImplementedError(('class2', cls)) else: if is_optional_type(cls): item, _ = get_last_args(cls) trc('1').debug('%s %s', item, _) r = auto_any(item) if isinstance(r, schema.Nullable): r.nullable = True else: raise NotImplementedError('can not be nullable') trc('2').debug('%s', r) return r else: raise NotImplementedError(('class', cls))
def dict_to_alfacase_schema(type_: type, indent: int) -> str: key_type, value_type = (typing_inspect.get_last_args(type_) if sys.version_info.minor == 6 else typing_inspect.get_args(type_)) body_indent = indent + 1 value_schema = _get_attr_value(value_type, indent=body_indent) if "\n" in value_schema: lines = [ "MapPattern(", f"{INDENTANTION * body_indent}Str(),", f"{INDENTANTION * body_indent}{value_schema},", f"{INDENTANTION * indent})", ] return "\n".join(lines) else: return f"MapPattern(Str(), {value_schema})"
def test_last_args(self): T = TypeVar('T') S = TypeVar('S') self.assertEqual(get_last_args(int), ()) self.assertEqual(get_last_args(Union), ()) if WITH_CLASSVAR: self.assertEqual(get_last_args(ClassVar[int]), (int,)) self.assertEqual(get_last_args(Union[T, int]), (T, int)) self.assertEqual(get_last_args(Union[str, int]), (str, int)) self.assertEqual(get_last_args(Tuple[T, int]), (T, int)) self.assertEqual(get_last_args(Tuple[str, int]), (str, int)) self.assertEqual(get_last_args(Generic[T]), (T, )) if GENERIC_TUPLE_PARAMETRIZABLE: tp = Iterable[Tuple[T, S]][int, T] self.assertEqual(get_last_args(tp), (int, T)) if LEGACY_TYPING: self.assertEqual(get_last_args(Callable[[T, S], int]), (T, S)) self.assertEqual(get_last_args(Callable[[], int]), ()) else: self.assertEqual(get_last_args(Callable[[T, S], int]), (T, S, int)) self.assertEqual(get_last_args(Callable[[], int]), (int,))
def get_args(self, xtype): return typing_inspect.get_last_args(xtype)
def resolve(self, cls: Type) -> Fac: """get a factory given an object cls""" # todo this needs to go, and we need to have proper instances of each separate `cls` mapper # def resolve_list(cls): vt, = get_args(cls) value = self.resolve(vt) if self.name.startswith('json'): return JsonAnyList(value) else: raise NotImplementedError(f'{self.name}') def resolve_dict(cls): kt, vt = get_args(cls) if self.name == 'json_into': assert issubclass(kt, str), (kt, ) key = self.resolve(kt) value = self.resolve(vt) if self.name.startswith('json'): return JsonAnyDict(key, value) else: raise NotImplementedError(f'{self.name}') if isinstance(cls, DeferredWrapper): cls = cls.type assert is_dataclass(cls), cls r: List[Fac] = [] for item in fields(cls): item: Field item_type = item.metadata.get(FIELD_OVERRIDE, item.type) item_factory = item.metadata.get(FIELD_FACTORY, {}) if isinstance(item_factory, Fac): pass elif isinstance(item_factory, dict): item_factory = item_factory.get(self.name, MISSING) else: assert False, repr(item_factory) if item_factory is MISSING: item_factory = self.resolve(item_type) is_optional = item.default is not MISSING # todo support optional if self.name == 'json_from': r.append( AnyAnyField(item.name, AnyAnyItem(item.name, item_factory))) elif self.name == 'json_into': r.append( AnyAnyField(item.name, AnyAnyAttr(item.name, item_factory))) else: raise NotImplementedError((self.name, None)) if self.name == 'json_from': return AnyFromStruct(r, cls) elif self.name == 'json_into': return AnyIntoStruct( r, cls, ) else: raise NotImplementedError((self.name, None)) elif isinstance(cls, Args): dx: CallableSerializers = getattr(cls.type, DECL_CALLABLE_ATTR) return dx.parameters[self.name] elif isinstance(cls, Return): dx: CallableSerializers = getattr(cls.type, DECL_CALLABLE_ATTR) return dx.return_[self.name] elif is_serializable(cls): return Ref(self.name, cls.__module__ + '.' + cls.__name__) elif inspect.isclass(cls): if issubclass(cls, bool): return This(bool) elif issubclass(cls, float): return This(float) elif issubclass(cls, int): return This(int) elif issubclass(cls, str): return This(str) elif issubclass(cls, datetime): if self.name == 'json_from': return JsonFromDateTime() elif self.name == 'json_into': return JsonIntoDateTime() else: raise NotImplementedError(f'{self.name}') elif issubclass(cls, timedelta): if self.name == 'json_from': return JsonFromTimeDelta() elif self.name == 'json_into': return JsonIntoTimeDelta() else: raise NotImplementedError(f'{self.name}') elif issubclass(cls, List): return resolve_list(cls) elif issubclass(cls, Dict): return resolve_dict(cls) elif issubclass(cls, Enum): clss = set(item.value.__class__ for item in cls) assert len(clss) == 1, clss t = clss.pop() fac = self.resolve(t) if self.name == 'json_from': return AnyFromEnum(cls, fac) elif self.name == 'json_into': return AnyIntoEnum(cls, fac) else: raise NotImplementedError(('class4', cls)) elif is_dataclass(cls): return Ref(self.name, cls.__module__ + '.' + cls.__name__) else: raise NotImplementedError(('class2', cls)) elif isinstance(cls, ForwardRef): if sys.version_info >= (3, 7): t = cls._evaluate(self.globals, self.locals) else: t = cls._eval_type(self.globals, self.locals) return self.resolve(t) elif isinstance(cls, str): code = compile(cls, '<string>', 'eval') t = eval(code, self.globals, self.locals) return self.resolve(t) else: if is_optional_type(cls): if sys.version_info >= (3, 7): item, _ = get_args(cls) else: item, _ = get_last_args(cls) value = self.resolve(item) if self.name.startswith('json'): return JsonAnyOptional(value) else: raise NotImplementedError(f'{self.name}') elif sys.version_info >= (3, 7) and hasattr(cls, '__origin__'): if cls.__origin__ is list: return resolve_list(cls) elif cls.__origin__ is dict: return resolve_dict(cls) else: raise NotImplementedError(('class', cls, cls.__class__))