Skip to content

Instantly share code, notes, and snippets.

@tonnydourado
Last active December 8, 2017 03:57
Show Gist options
  • Save tonnydourado/0ac11c518d5331d182d3ef62563d649c to your computer and use it in GitHub Desktop.
Save tonnydourado/0ac11c518d5331d182d3ef62563d649c to your computer and use it in GitHub Desktop.
import json
from functools import wraps
from typing import Dict
from attr import attrs
from attr.validators import in_
from cattr import typed, structure, unstructure
# Don't at me, I like methods:
def to_dict(self):
return unstructure(self)
def to_json(self):
return json.dumps(self.to_dict())
# Not so sure about the name, but it's 1 AM, so go away:
def validated(maybe_cls=None, *attrs_args, **attrs_kwargs):
"""
Adds type checking on instantiation to attrs-enabled classes
:param maybe_cls: maybe the class being decorated, maybe not, depending on
how the decorator is used
"""
def wrapper(cls):
attrs_cls = attrs(**attrs_kwargs)(cls)
attrs_cls.to_dict = to_dict
attrs_cls.to_json = to_json
@wraps(attrs_cls)
def new(**kwargs):
# Dealing with defaults because cattrs doesn't =/
for attribute in attrs_cls.__attrs_attrs__:
if attribute.name not in kwargs.keys():
kwargs[attribute.name] = attribute.default
return structure(kwargs, attrs_cls)
# Setting the class being used, just in case we need it:
new.cls = attrs_cls
return new
# maybe_cls's type depends on the usage of the decorator. It's a class
# if it's used as `@validated` but ``None`` if used as `@validated()`.
if maybe_cls is None:
return wrapper
else:
return wrapper(maybe_cls)
@validated
class Stuff(object):
integer = typed(int, validator=in_([1, 2]))
mapping = typed(Dict, default={})
string = typed(str, default='')
print(Stuff, Stuff.cls)
print(Stuff(integer=1))
print(Stuff(integer='1'))
print(Stuff(integer=1, string=234))
try:
print(Stuff(integer='asdsd'))
except Exception as e:
print(type(e), e)
# Apparently cattrs deals with mutable defaults, so we don't neet attr.Factory:
s1, s2 = Stuff(integer=1), Stuff(integer=2)
print(s1, s2)
s1.mapping['adasd'] = 1
print(s1, s2, s1.mapping is s2.mapping)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment