codekingpro/portable-devtools
115k
1import inspect2import sys3from datetime import datetime, timezone4from collections import Counter5from dataclasses import is_dataclass # type: ignore6from typing import (Collection, Mapping, Optional, TypeVar, Any, Type, Tuple,7 Union, cast)8 9 10def _get_type_cons(type_):11 """More spaghetti logic for 3.6 vs. 3.7"""12 if sys.version_info.minor == 6:13 try:14 cons = type_.__extra__15 except AttributeError:16 try:17 cons = type_.__origin__18 except AttributeError:19 cons = type_20 else:21 cons = type_ if cons is None else cons22 else:23 try:24 cons = type_.__origin__ if cons is None else cons25 except AttributeError:26 cons = type_27 else:28 cons = type_.__origin__29 return cons30 31 32_NO_TYPE_ORIGIN = object()33 34 35def _get_type_origin(type_):36 """Some spaghetti logic to accommodate differences between 3.6 and 3.7 in37 the typing api"""38 try:39 origin = type_.__origin__40 except AttributeError:41 # Issue #341 and PR #346:42 # For some cases, the type_.__origin__ exists but is set to None43 origin = _NO_TYPE_ORIGIN44 45 if sys.version_info.minor == 6:46 try:47 origin = type_.__extra__48 except AttributeError:49 origin = type_50 else:51 origin = type_ if origin in (None, _NO_TYPE_ORIGIN) else origin52 elif origin is _NO_TYPE_ORIGIN:53 origin = type_54 return origin55 56 57def _hasargs(type_, *args):58 try:59 res = all(arg in type_.__args__ for arg in args)60 except AttributeError:61 return False62 except TypeError:63 if (type_.__args__ is None):64 return False65 else:66 raise67 else:68 return res69 70 71class _NoArgs(object):72 def __bool__(self):73 return False74 75 def __len__(self):76 return 077 78 def __iter__(self):79 return self80 81 def __next__(self):82 raise StopIteration83 84 85_NO_ARGS = _NoArgs()86 87 88def _get_type_args(tp: Type, default: Union[Tuple[Type, ...], _NoArgs] = _NO_ARGS) -> \89 Union[Tuple[Type, ...], _NoArgs]:90 if hasattr(tp, '__args__'):91 if tp.__args__ is not None:92 return tp.__args__93 return default94 95 96def _get_type_arg_param(tp: Type, index: int) -> Union[Type, _NoArgs]:97 _args = _get_type_args(tp)98 if _args is not _NO_ARGS:99 try:100 return cast(Tuple[Type, ...], _args)[index]101 except (TypeError, IndexError, NotImplementedError):102 pass103 104 return _NO_ARGS105 106 107def _isinstance_safe(o, t):108 try:109 result = isinstance(o, t)110 except Exception:111 return False112 else:113 return result114 115 116def _issubclass_safe(cls, classinfo):117 try:118 return issubclass(cls, classinfo)119 except Exception:120 return (_is_new_type_subclass_safe(cls, classinfo)121 if _is_new_type(cls)122 else False)123 124 125def _is_new_type_subclass_safe(cls, classinfo):126 super_type = getattr(cls, "__supertype__", None)127 128 if super_type:129 return _is_new_type_subclass_safe(super_type, classinfo)130 131 try:132 return issubclass(cls, classinfo)133 except Exception:134 return False135 136 137def _is_new_type(type_):138 return inspect.isfunction(type_) and hasattr(type_, "__supertype__")139 140 141def _is_optional(type_):142 return (_issubclass_safe(type_, Optional) or143 _hasargs(type_, type(None)) or144 type_ is Any)145 146 147def _is_counter(type_):148 return _issubclass_safe(_get_type_origin(type_), Counter)149 150 151def _is_mapping(type_):152 return _issubclass_safe(_get_type_origin(type_), Mapping)153 154 155def _is_collection(type_):156 return _issubclass_safe(_get_type_origin(type_), Collection)157 158 159def _is_tuple(type_):160 return _issubclass_safe(_get_type_origin(type_), Tuple)161 162 163def _is_nonstr_collection(type_):164 return (_issubclass_safe(_get_type_origin(type_), Collection)165 and not _issubclass_safe(type_, str))166 167 168def _is_generic_dataclass(type_):169 return is_dataclass(_get_type_origin(type_))170 171 172def _timestamp_to_dt_aware(timestamp: float):173 tz = datetime.now(timezone.utc).astimezone().tzinfo174 dt = datetime.fromtimestamp(timestamp, tz=tz)175 return dt176 177 178def _undefined_parameter_action_safe(cls):179 try:180 if cls.dataclass_json_config is None:181 return182 action_enum = cls.dataclass_json_config['undefined']183 except (AttributeError, KeyError):184 return185 186 if action_enum is None or action_enum.value is None:187 return188 189 return action_enum190 191 192def _handle_undefined_parameters_safe(cls, kvs, usage: str):193 """194 Checks if an undefined parameters action is defined and performs the195 according action.196 """197 undefined_parameter_action = _undefined_parameter_action_safe(cls)198 usage = usage.lower()199 if undefined_parameter_action is None:200 return kvs if usage != "init" else cls.__init__201 if usage == "from":202 return undefined_parameter_action.value.handle_from_dict(cls=cls,203 kvs=kvs)204 elif usage == "to":205 return undefined_parameter_action.value.handle_to_dict(obj=cls,206 kvs=kvs)207 elif usage == "dump":208 return undefined_parameter_action.value.handle_dump(obj=cls)209 elif usage == "init":210 return undefined_parameter_action.value.create_init(obj=cls)211 else:212 raise ValueError(213 f"usage must be one of ['to', 'from', 'dump', 'init'], "214 f"but is '{usage}'")215 216 217# Define a type for the CatchAll field218# https://stackoverflow.com/questions/59360567/define-a-custom-type-that-behaves-like-typing-any219CatchAllVar = TypeVar("CatchAllVar", bound=Mapping)220 