Skip to content

Instantly share code, notes, and snippets.

@jsborjesson
Created January 18, 2014 21:38
Show Gist options
  • Save jsborjesson/8496924 to your computer and use it in GitHub Desktop.
Save jsborjesson/8496924 to your computer and use it in GitHub Desktop.

Revisions

  1. Jimmy Börjesson created this gist Jan 18, 2014.
    111 changes: 111 additions & 0 deletions descriptors.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,111 @@
    import re
    from weakref import WeakKeyDictionary

    class AdvancedDescriptor(object):
    """
    Base class for descriptors, is hard to understand but works for most cases.
    from https://www.youtube.com/watch?v=P92z7m-kZpc
    """

    def __init__(self, name=None):
    self.name = self.mangle(name)


    def __get__(self, instance, owner):
    if instance is None:
    raise AttributeError('Can only be accessed from instance')
    if self.name not in instance.__dict__:
    raise AttributeError
    return instance.__dict__[self.name]


    def __set__(self, instance, value):
    if self.name == None: self.fetchattr(instance)
    instance.__dict__[self.name] = value


    def __delete__(self, instance):
    del instance.__dict__[self.name]


    def fetchattr(self, instance):
    for attr in instance.__class__.__dict__:
    if attr.startswith('__'): continue
    obj = instance.__class__.__dict__[attr]
    if obj == self:
    self.name = self.mangle(attr)
    break


    def mangle(self, name):
    """Mangle name to __name."""
    if name != None:
    if name.startswith('__'):
    raise AttributeError('Name conflict')
    elif name.startswith('_'):
    return '_' + name
    else:
    return '__' + name
    return name



    class ValidationDescriptor(object):
    """Simple base descriptor with support for validation."""

    def __init__(self, default=None):
    self.default = default
    self.data = WeakKeyDictionary()

    def __get__(self, instance, owner):
    return self.data.get(instance, self.default)

    def __set__(self, instance, value):
    self.data[instance] = self.validate(value)

    def __delete__(self, instance):
    del self.data[instance]

    def validate(self, value):
    """Can either raise an exception or return a valid value."""
    raise NotImplementedError('You need to implement a validate method')




    class RegexValidator(ValidationDescriptor):
    """Descriptor that validates a regex when set."""

    def __init__(self, regex=None):
    self.regex = re.compile(regex) if isinstance(regex, str) else regex
    super(RegexValidator, self).__init__()

    def validate(self, value):
    if self.regex.match(value) != None:
    return value
    else:
    raise TypeError('"{}" is not valid.'.format(value))


    class Person(object):

    email = RegexValidator(r'^(\w*)@(\w*)\.(\w{3})$')

    def __init__(self, name, email):
    self.name = name
    self.email = email

    def __str__(self):
    return '{}: {}'.format(self.name, self.email)


    # demo
    if __name__ == '__main__':

    d = Person('Alice', '[email protected]')
    print(d)
    try:
    p = Person('Wallace', 'notanemail.com')
    except Exception as e:
    print(e)