def patch_class(clas: type, cfg: Config): if clas in GlobalPatchedList: return clas GlobalPatchedList.add(clas) try: ctx = DefaultCreationContext( typevars=dict(), declared_location=Location( file=inspect.getfile(clas), line_no=inspect.getsourcelines(clas)[1], line_span=len(inspect.getsourcelines(clas)[0]), ), checkedpkgprefixes=cfg.checkedprefixes) except (TypeError, OSError) as e: # Built in types ctx = DefaultCreationContext( typevars=dict(), declared_location=Location(file="<not found>", line_no=0, line_span=1), checkedpkgprefixes=cfg.checkedprefixes, ) setattr(clas, '__patched', True) is_protocol = hasattr(clas, 'mro') and Protocol in clas.mro() if hasattr(clas, '__class_getitem__') and not is_protocol: original = clas.__class_getitem__ setattr(clas, '__class_getitem__', lambda *args: WrappedGenericAlias(original(*args), ctx))
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: t, i = err.next_type_and_indicator() return err.with_frame( Frame(t, i, responsable=Location.from_stack(self.caller), declared=Location.from_code(self.declared)))
def assertBlame(self, cm, blamed): ok = cm.exception.last_responsable() in Location.from_code(blamed) if not ok: print(cm.exception.last_responsable()) print("not in") print(Location.from_code(blamed)) self.assertTrue(ok)
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: (t, i) = err.next_type_and_indicator() return err.with_frame( Frame(t, i, declared=None, responsable=Location(file="dummy", line_no=0, line_span=1)))
def find_location(fn) -> Optional[Location]: fn = WrappedFunction.find_original(fn) try: return Location( file=inspect.getfile(fn), line_no=inspect.getsourcelines(fn)[1], source_line="".join(inspect.getsourcelines(fn)[0]), ) except: # Failes on builtins return None
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: (next_ty, indicator) = err.next_type_and_indicator() error_id = IndicatorStr(next_ty, indicator) original = WrappedFunction.find_original(self.fn) try: signature = inspect.signature(original) except ValueError: # fails on some built-ins signature = inspect.signature(self.fn) wf = None if (hasattr(self.fn, '__wf')): wf = getattr(self.fn, '__wf') elif isinstance(self.fn, WrappedFunction): wf = self.fn arglist = [] for name in signature.parameters: if name is self.argument_name: arglist.append(IndicatorStr(f"{name}: ") + error_id) else: if wf is not None: arglist.append( IndicatorStr( f"{name}: {wf.checker_for(name).describe()}")) else: arglist.append(IndicatorStr(f"{name}")) id = IndicatorStr(f"{format_name(original)}(") + IndicatorStr( ", ").join(arglist) if wf is not None: id += IndicatorStr(f") -> {wf.checker_for('return').describe()}") else: id += IndicatorStr(f")") if self.declared is None: declared = WrappedFunction.find_location(self.fn) else: declared = self.declared if self.stack is not None: responsable = Location.from_stack(self.stack) else: responsable = None frame = Frame(id.ty, id.indicator, declared=declared, responsable=responsable) return err.with_frame(frame)
def responsable(self) -> Optional[Location]: try: if hasattr(self.iter, 'gi_frame'): return Location( file=inspect.getfile(self.iter.gi_frame), line_no=inspect.getsourcelines(self.iter.gi_frame)[1], line_span=len(inspect.getsourcelines(self.iter.gi_frame)[0]), ) except OSError: # this call does not work all the time pass except TypeError: pass return None
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: (type_declared, indicator_line) = self.declared_and_indicator(err) declared = WrappedFunction.find_location(self.fn.inner) responsable = Location.from_stack(self.stack) frame = Frame( type_declared, indicator_line, declared=declared, responsable=responsable, ) err = err.with_frame(frame) err = err.with_inverted_responsibility_type() return self.upper.wrap(err)
def test_standalone(self): ch = untypy.checker(lambda: int, TestStandaloneChecker.test_standalone) self.assertEqual(ch(42), 42) def myfunc(x): ch(x) with self.assertRaises(UntypyTypeError) as cm: myfunc("hello") self.assertEqual(cm.exception.expected, 'int') self.assertEqual( cm.exception.last_declared(), Location.from_code(TestStandaloneChecker.test_standalone)) self.assertIn('myfunc("hello")', cm.exception.last_responsable().source_lines)
def _find_bound_typevars(clas: type) -> (type, Dict[TypeVar, Any]): if not hasattr(clas, '__args__') or not hasattr(clas, '__origin__'): return (clas, dict()) if not hasattr(clas.__origin__, '__parameters__'): return (clas, dict()) keys = clas.__origin__.__parameters__ values = clas.__args__ if len(keys) != len(values): raise UntypyAttributeError( f"Some unbound Parameters in {clas.__name__}. " f"keys={keys} do not match values={values}.", [ Location(file=inspect.getfile(clas), line_no=inspect.getsourcelines(clas)[1], source_line="".join(inspect.getsourcelines(clas)[0])) ]) return (clas.__origin__, dict(zip(keys, values)))
def get_checker(self): if self._checker: return self._checker ctx = DefaultCreationContext( typevars=dict(), declared_location=Location.from_code(self.declared), checkedpkgprefixes=self.cfg.checkedprefixes) try: annotation = self.annotation() except NameError as ne: raise ctx.wrap( UntypyNameError( f"{ne}.\nType annotation could not be resolved.")) checker = ctx.find_checker(annotation) if checker is None: raise ctx.wrap( UntypyAttributeError( f"\n\tUnsupported type annotation: {self.annotation}\n")) self._checker = checker return checker
def __init__(self, typevars: dict[TypeVar, Any] = dict()): super().__init__(typevars.copy(), Location(file="dummy", line_no=0, line_span=1), checkedpkgprefixes=["test"])
def wrap(self, err: UntypyTypeError) -> UntypyTypeError: next_type, indicator = err.next_type_and_indicator() return err.with_frame( Frame(f"list[{next_type}]", (" " * len("list[") + indicator), declared=self.declared, responsable=Location.from_stack(self.stack)))
def responsable(self) -> Optional[Location]: return Location.from_stack(self.stack)
def wrap_class(a: type, cfg: Config) -> Callable: return WrappedType( a, DefaultCreationContext(typevars=dict(), declared_location=Location.from_code(a), checkedpkgprefixes=cfg.checkedprefixes))