Last active
September 19, 2016 10:46
-
-
Save ztane/acf66661505f5344ac0d21ab86fa3f6f to your computer and use it in GitHub Desktop.
Typing testing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import warnings | |
import inspect | |
NONE = object() | |
class TypingWarning(Warning): | |
pass | |
class CheckedMeta(type): | |
def __new__(mcl, name, bases, nmspc): | |
new_type = super(CheckedMeta, mcl).__new__(mcl, name, bases, nmspc) | |
full_annotations = {} | |
for i in new_type.__mro__: | |
for k, v in getattr(i, '__annotations__', {}).items(): | |
if k not in full_annotations: | |
full_annotations[k] = v | |
if hasattr(new_type, '__dict__'): | |
new_type.__full_annotations__ = full_annotations | |
return new_type | |
def __init__(cls, name, bases, nmspc): | |
super().__init__(name, bases, nmspc) | |
full_annotations = getattr(cls, '__full_annotations__', {}) | |
for k, v in full_annotations.items(): | |
val = getattr(cls, k, NONE) | |
if val is NONE or hasattr(val, '__get__') or hasattr(val, '__set__'): | |
continue | |
if not isinstance(val, v): | |
warnings.warn(f'{cls.__name__}.{k} value {val} doesn\'t' | |
f' match annotation {v}', TypingWarning, | |
stacklevel=2) | |
wrapped_attr_name = f'_checkedmeta_{k}' | |
def make_getter(wrapped_attr_name): | |
return lambda self: getattr(self, wrapped_attr_name) | |
def make_setter(wrapped_attr_name, type): | |
def setter(self, value): | |
if not isinstance(value, type): | |
warnings.warn(f'{cls.__name__}.{k} value {val} doesn\'t' | |
f' match annotation {v}', TypingWarning, | |
stacklevel=2) | |
setattr(self, wrapped_attr_name, value) | |
return setter | |
def make_deleter(wrapped_attr_name): | |
return lambda self: delattr(self, wrapped_attr_name) | |
wrapped_descriptor = property(fget=make_getter(wrapped_attr_name), | |
fset=make_setter(wrapped_attr_name, v), | |
fdel=make_deleter(wrapped_attr_name)) | |
setattr(cls, wrapped_attr_name, val) | |
setattr(cls, k, wrapped_descriptor) | |
class CheckedBase(metaclass=CheckedMeta): | |
pass | |
class InstanceTest(CheckedBase): | |
a: int | |
b: str = '55' | |
class InstanceTest2(InstanceTest): | |
a: str = '42' | |
c: tuple = [1, 2] | |
x = InstanceTest2() | |
x.b = 55 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment