Last active
October 8, 2015 00:10
-
-
Save jomido/3247456 to your computer and use it in GitHub Desktop.
Python Type Constraints (with Reserved Attributes)
This file contains hidden or 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
| class SchemaFailException(Exception): | |
| def __init__(self, message, attr, valid_types): | |
| Exception.__init__(self, message) | |
| self.attr = attr | |
| self.valid_types = valid_types | |
| class ReservedAttributeException(Exception): | |
| def __init__(self, message, attr): | |
| Exception.__init__(self, message) | |
| self.attr = attr | |
| def _is_reserved_function(string_list): | |
| def _is_reserved(key): | |
| if key in string_list: | |
| return True | |
| return _is_reserved | |
| def _fails_schema_function(schema_dict): | |
| def _fails_schema(key, value): | |
| if key not in schema_dict.keys(): | |
| return False | |
| if type(value) not in schema_dict[key]: | |
| return True | |
| return _fails_schema | |
| class Typed(type): | |
| @classmethod | |
| def __prepare__(metacls, name, bases, **kwds): | |
| schema = {} | |
| for base in bases: | |
| if hasattr(base, "_schema"): | |
| schema.update(base._schema) | |
| if "schema" in kwds: | |
| schema.update(kwds["schema"]) | |
| class base_obj_dict(dict): | |
| def __init__(self, metacls, cls): | |
| self._schema = schema | |
| self.reserved = [ | |
| "_metacls", | |
| "_schema", | |
| "_cls", | |
| "_reserved", | |
| "_is_reserved", | |
| "_fails_schema" | |
| ] | |
| self._is_reserved = _is_reserved_function(self.reserved) | |
| self._fails_schema = _fails_schema_function(schema) | |
| super().__init__({ | |
| "_metacls": metacls, | |
| "_cls": cls, | |
| "_schema": schema, | |
| "_reserved": self.reserved, | |
| "_is_reserved": self._is_reserved, | |
| "_fails_schema": self._fails_schema | |
| }) | |
| def __setitem__(self, key, value): | |
| if self._is_reserved(key): | |
| msg = "The attribute '{}'' is reserved and cannot be " | |
| "defined.".format(key) | |
| raise ReservedAttributeException(msg, key) | |
| if self._fails_schema(key, value): | |
| valid = [str(t) for t in self._schema[key]] | |
| msg = "The attribute '{}' can only be these types: {}.\n" | |
| "It therefore cannot be defined as {}, which is of type " | |
| "{}.".format(key, ', '.join(valid), value, type(value)) | |
| raise SchemaFailException(msg, key, self._schema[key]) | |
| super().__setitem__(key, value) | |
| return base_obj_dict(metacls, name) | |
| def __new__(cls, name, bases, attrs, **options): | |
| # This is only necessary because type.__new__() doesn't know how to | |
| # handle the extra arguments re: | |
| # http://martyalchin.com/2011/jan/20/class-level-keyword-arguments/ | |
| # (same for __init__) | |
| return type.__new__(cls, name, bases, attrs) | |
| def __init__(cls, name, bases, attrs, **kwargs): | |
| pass | |
| def __setattr__(cls, key, value): | |
| if cls._is_reserved(key): | |
| msg = "The attribute '{}'' is reserved and cannot be set".format( | |
| key) | |
| raise ReservedAttributeException(msg, key) | |
| if cls._fails_schema(key, value): | |
| valid = [str(t) for t in cls._schema[key]] | |
| msg = "The attribute '{}' can only be these types: {}.\n" | |
| "It therefore cannot be set to {}, which is of type {}".format( | |
| key, ', '.join(valid), value, type(value)) | |
| raise SchemaFailException(msg, key, cls._schema[key]) | |
| super().__setattr__(key, value) | |
| class Typed(metaclass=Typed): | |
| def __setattr__(self, key, value): | |
| cls = self.__class__ | |
| if cls._is_reserved(key): | |
| msg = "The attribute '{}'' is reserved and cannot be " \ | |
| "set.".format(key) | |
| raise ReservedAttributeException(msg, key) | |
| if cls._fails_schema(key, value): | |
| valid = [str(t) for t in cls._schema[key]] | |
| msg = "The attribute '{}' can only be these types: {}.\n" | |
| "It therefore cannot be set to {}, which is of type {}.".format( | |
| key, ', '.join(valid), value, type(value)) | |
| raise SchemaFailException(msg, key, cls._schema[key]) | |
| super().__setattr__(key, value) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment