def serde(cls): """ Class decorator for creating serializable classes. """ if not attr.has(cls): cls = attr.s(auto_attribs=True, slots=True)(cls) return cls
def attrs(class_): """ Like attr.s with slots=True, but with attributes extracted from __init__ method signature. slots=True ensures that signature matches what really happens (we can't define different attributes on self). It is useful if we still want __init__ for proper type-checking and do not want to repeat attribute definitions in the class body. """ attrs_kwargs = {} for method, kw_name in [ ('__repr__', 'repr'), ('__eq__', 'cmp'), ('__hash__', 'hash'), ]: if method in class_.__dict__: # Allow to redefine a special method (or else attr.s will do it) attrs_kwargs[kw_name] = False init_args = inspect.getargspec(class_.__init__) defaults_shift = len(init_args.args) - len(init_args.defaults or []) - 1 these = {} for idx, arg in enumerate(init_args.args[1:]): attrib_kwargs = {} if idx >= defaults_shift: attrib_kwargs['default'] = init_args.defaults[idx - defaults_shift] these[arg] = attr.ib(**attrib_kwargs) return attr.s(class_, these=these, init=False, slots=True, **attrs_kwargs) # type: ignore
def hashableAttrs(cls=None, repr=True): """A fully hashable attrs decorator. Converts unhashable attributes, e.g. lists, to hashable ones, e.g. tuples. """ if cls is None: return partial(hashableAttrs, repr=repr) def hash_(self): obj = self for field in attr.fields(self.__class__): value = getattr(self, field.name) if isinstance(value, (list, set)): new_value = tuple(value) else: new_value = value if new_value != value: obj = attr.evolve(obj, **{field.name: new_value}) return hash( attr.astuple(obj, recurse=False, filter=lambda a, v: a.hash is not False)) decorated = attr.s(cls, hash=False, repr=repr) decorated.__hash__ = hash_ return decorated
def __new__(mcls, name, bases, attrs): if bases: for k, f in attrs.items(): annotations = attrs.get("__annotations__", {}) _implement_converter(f, k, annotations) _implement_validators(f, k, annotations) return attr.s(super().__new__(mcls, name, bases, attrs))
def entity(cls): for k, v in cls.__dict__.items(): if hasattr(v, 'init') and isinstance(v.init, bool): v.init = False result = attr.make_class(cls.__name__, (), bases=(_Signature, attr.s(cls), _Entity)) return functools.wraps(cls, updated=())(result)
def wraps(cls): req = required or [] setattr(cls, "additional_properties", additional_properties) def jo_schema(cls): sc = Object( additional_properties=additional_properties, min_properties=min_properties, max_properties=max_properties, xml=xml, ) sc.properties = {} attributes = cls.__attrs_attrs__ for attrib in attributes: psc = attrib.metadata[JO_SCHEMA] is_required = attrib.metadata.get(JO_REQUIRED, False) field_name = camel_case( attrib.name) if camel_case_props else attrib.name if is_required and field_name not in req: req.append(field_name) sc.properties[field_name] = psc if req: sc.required = req return sc setattr(cls, "jo_schema", classmethod(jo_schema)) return attr.s(cls)
def test_respects_add_arguments(self, arg_name, method_name): """ If a certain `XXX` is `False`, `__XXX__` is not added to the class. """ # Set the method name to a sentinel and check whether it has been # overwritten afterwards. sentinel = object() am_args = { "repr": True, "eq": True, "order": True, "hash": True, "init": True, } am_args[arg_name] = False if arg_name == "eq": am_args["order"] = False class C(object): x = attr.ib() setattr(C, method_name, sentinel) C = attr.s(**am_args)(C) assert sentinel == getattr(C, method_name)
def _make_constructor(name, type_, attrs, kwargs): """Create a type specific to the constructor.""" d = dict(attrs) d['_sumtype_attribs'] = [x for x in attrs] t = type(name, (type_,), d) t = attr.s(t, repr_ns=type_.__name__, **kwargs) return t
def wrap(cls): cls_with_attrs = _attr.s(cls, these, repr_ns, repr, cmp, hash, init, slots, frozen, str) for embedded_attr in _get_embedded_attrs(cls_with_attrs): for promoted_name in _attrs_to_promote(embedded_attr): _try_to_promote(cls_with_attrs, promoted_name, embedded_attr) return cls_with_attrs
def s(*args, **kwargs): # A few features we'd like to use are not available on older Python/attrs # versions, so we just silently disable them. # # Note this means we're technically exporting a different API on # Py2 vs Py3, i.e. keywords-only isn't enforced on Py2. # This isn't ideal, but is mitigated by the fact that any new code # using this library will surely not be tested solely on Py2. kwargs = kwargs.copy() if "slots" not in kwargs: # Slotted classes should be used where possible, but there # are a few cases where it won't work, so the possibility # is left open to disable it via slots=False. kwargs["slots"] = True if "repr" not in kwargs: # We provide our own __repr__ implementation in PulpObject, so # don't use the attrs-generated repr by default kwargs["repr"] = False if "kw_only" in kwargs and sys.version_info < (3, ): # pragma: no cover # This is only implemented for Python 3. # attrs will raise if kw_only is provided on Py2. del kwargs["kw_only"] return attr.s(*args, **kwargs)
def ast_class(cls): cls.__xor__ = sugar.xor cls.__or__ = _or cls.__and__ = _and cls.__invert__ = _neg cls.__call__ = _eval cls.__rshift__ = _timeshift cls.__getitem__ = _inline_context cls.walk = _walk cls.params = property(_params) cls.atomic_predicates = property(_atomic_predicates) cls.evolve = attr.evolve cls.iff = sugar.iff cls.implies = sugar.implies # Avoid circular dependence. cls.weak_until = lambda a, b: WeakUntil(a, b) cls.until = sugar.until cls.timed_until = sugar.timed_until cls.always = sugar.alw cls.eventually = sugar.env if not hasattr(cls, "children"): cls.children = property(lambda _: ()) return attr.s(frozen=True, auto_attribs=True, repr=False, slots=True)(cls)
def wrapper(cl): res = attr.s(slots=True, frozen=frozen, repr=repr)(cl) res.__fb_module__ = modules[fb_cl.__module__] res.__fb_class__ = fb_cl _make_fb_functions(res) return res
def typed_attrs(cls): """ Created a type-validated attrs class. Attributes are set to their default values if they're assigned `None` to. """ for attrib in cls.__dict__.values(): try: if not attrib.type: continue validator = attrib._validator except AttributeError: continue instance_of = attr.validators.instance_of(attrib.type) attrib._validator = (attr.validators.and_(validator, instance_of) if validator else instance_of) if attrib._default != attr.NOTHING: def converter(default): """ Create a converter that interprets `None` as "use default". Required in order to create a new lexical scope, see https://stackoverflow.com/a/233835 """ return lambda x: default if x is None else x attrib.converter = converter(attrib._default) return attr.s(cls)
def otterize(cls,*args,**kwargs): '''Wrap all Configurations with this decorator with the following behavior 1) we use the callback when any property changes 2) repr is default 3) hash is by object identity''' acls = attr.s(cls, on_setattr= property_changed, repr=False,eq=False, *args,**kwargs) return acls
def wrapper(fn): name = fn.__name__ operators = operator_pool if operator_pool is not None else _OPERATOR_POOL KW_ONLY = inspect.Parameter.KEYWORD_ONLY signature = inspect.signature(fn).parameters attrs = [attr for attr in signature.values() if attr.kind == KW_ONLY] attributes = { "__doc__": inspect.getdoc(fn), "__module__": fn.__module__, "_impl": staticmethod(fn), "_operators": operators, } for a in attrs: attributes[a.name] = _define_attribute(a) bases, inputs = _process_parameters(signature, attributes) attributes["_bases"] = bases attributes["_inputs"] = inputs defn = attr.s(type(name, (Defn,), attributes)) if operation is not None: _define_operator(defn, operation, operators) return defn
def wrapper(c): c.__attrs_post_init__ = _set_backlinks c.type = attributes.const(typ) c.id = attributes.idfield(typ) c._m = attr.ib(repr=None, default=None) c = attr.s(cmp=False)(c) _type_to_cls[typ] = c return c
def test_nonslots_these(): """ Enhancing a dict class using 'these' works. This will actually *replace* the class with another one, using slots. """ class SimpleOrdinaryClass(object): def __init__(self, x, y, z): self.x = x self.y = y self.z = z def method(self): return self.x @classmethod def classmethod(cls): return "clsmethod" @staticmethod def staticmethod(): return "staticmethod" C2Slots = attr.s( these={ "x": attr.ib(), "y": attr.ib(), "z": attr.ib() }, init=False, slots=True, hash=True, )(SimpleOrdinaryClass) c2 = C2Slots(x=1, y=2, z="test") assert 1 == c2.x assert 2 == c2.y assert "test" == c2.z with pytest.raises(AttributeError): c2.t = "test" # We have slots now. assert 1 == c2.method() assert "clsmethod" == c2.classmethod() assert "staticmethod" == c2.staticmethod() assert set(["__weakref__", "x", "y", "z"]) == set(C2Slots.__slots__) c3 = C2Slots(x=1, y=3, z="test") assert c3 > c2 c2_ = C2Slots(x=1, y=2, z="test") assert c2 == c2_ assert "SimpleOrdinaryClass(x=1, y=2, z='test')" == repr(c2) hash(c2) # Just to assert it doesn't raise. assert {"x": 1, "y": 2, "z": "test"} == attr.asdict(c2)
def dataclass(cls): """ Decorator for quickly creating defining data classes. Really just wrapper around `attr.s`, an alternative to the std libs dataclasses. """ if not attr.has(cls): cls = attr.s(auto_attribs=True, slots=True)(cls) return cls
def wrap(cls): def from_environ(cls, environ=os.environ): import environ as environ_config return environ_config.to_config(cls, environ) cls._prefix = prefix cls.from_environ = classmethod(from_environ) return attr.s(cls, slots=True)
def __init__(cls, name, bases, _dict): super(ScenarioMeta, cls).__init__(name, bases, _dict) # Make sure every sub-class is wrapped by `attr.s` cls = attr.s()(cls) # Do not register the base Scenario class # DEV: We cannot compare `cls` to `Scenario` since it doesn't exist yet if cls.__module__ != __name__: _register(cls)
def wrap(cls): if aaccessor is not None: if hasattr(cls, aaccessor): raise ValueError( f"{cls} already has an attribute with name {aaccessor}") setattr(cls, aaccessor, array_accessor()) attr_s = attr.s(**kwargs) return attr_s(cls)
def _make_attrs_class_wrapper(cls): for name, member in extra_members.items(): if name in cls.__dict__.keys(): raise ValueError( "Name clashing with a existing '{name}' member" " of the decorated class ".format(name=name)) setattr(cls, name, member) new_cls = attr.s(cls, **kwargs) return new_cls
def test_dict_patch_class(self): """ dict-classes are never replaced. """ class C(object): x = attr.ib() C_new = attr.s(C) assert C_new is C
def __new__(mcs, name: str, bases: tuple, namespace: dict): cls = super().__new__(mcs, name, bases, namespace) if name == "Entity": return cls attr_cls = attr.s(auto_attribs=True)(cls) if not any( Identity.is_identity(field) for field in attr.fields(attr_cls)): raise EntityWithoutIdentity return attr_cls
def _wrapper_model(cls): m3gr_instance = M3gr.instance() setattr(cls, "M3gr", m3gr_instance) _table_name = table_name or cls.__name__.split(".").pop().lower() setattr(cls, "table_name", _table_name) cls_obj = attr.s()(cls) assert not m3gr_instance.has_meta(_table_name), u"model has exsit" m3gr_instance.push_meta(_table_name, cls_obj) return cls_obj
def immutable(maybe_cls=None, eq=False, repr=False, **kwargs): return attr.s( maybe_cls=maybe_cls, frozen=True, eq=eq, order=False, hash=False, str=False, repr=repr, **kwargs )
def __new__(cls, name, bases, attrs): attrs['objects'] = ManagerDescriptor() mod = attr.s( super(ModelBase, cls).__new__(cls, name, bases, attrs) ) if 'Model' in [base.__name__ for base in bases]: ModelStoreMapping[mod.__name__] = Store() return mod
def __new__(mcs, name: str, bases: tuple, namespace: dict): cls = super().__new__(mcs, name, bases, namespace) if name == "ValueObject": return cls attr_cls = attr.s(auto_attribs=True)(cls) fields = attr.fields(attr_cls) if any(Identity.is_identity(field) for field in fields): raise ValueObjectWithIdentity if any(_is_nested_entity(field.type) for field in fields): raise EntityNestedInValueObject return attr_cls
def test_init_type_hints_fake_module(self): """ If you somehow set the __module__ to something that doesn't exist you'll lose __init__ resolution. """ class C: x = attr.ib(type="typing.List[int]") C.__module__ = "totally fake" C = attr.s(C) with pytest.raises(NameError): typing.get_type_hints(C.__init__)
def wrap(cls): def from_environ_fnc(cls, environ=os.environ): return __to_config(cls, environ) def generate_help_fnc(cls, **kwargs): return __generate_help(cls, **kwargs) cls._prefix = prefix if from_environ is not None: setattr(cls, from_environ, classmethod(from_environ_fnc)) if generate_help is not None: setattr(cls, generate_help, classmethod(generate_help_fnc)) return attr.s(cls, frozen=frozen, slots=True)
def dataclass_from_params(name: str, params: Parameters, module=None): attributes = {} for param in params: attributes[param.name] = attr.ib(default=param.default, type=param.type) # 'yaml_tag' is not processed by attr.s and thus ignored by visip code representer. # however it allows correct serialization to yaml # Note: replaced by the DataClassBase classproperty 'yaml_tag'. #attributes['yaml_tag'] = u'!{}'.format(name) data_class = type(name, (dtype.DataClassBase, ), attributes) if module: data_class.__module__ = module return attr.s(data_class)
def __new__(cls, *args, **kwargs) -> Any: # allow all items in Binary Object schema to be populated as optional # arguments to `__init__()` with sensible defaults. if cls is not GenericObjectMeta: attributes = { k: attr.ib( type=getattr(v, 'pythonic', type(None)), default=getattr(v, 'default', None), ) for k, v in cls.schema.items() } attributes.update({'version': attr.ib(type=int, default=1)}) cls = attr.s(cls, these=attributes) # skip parameters return super().__new__(cls)
def test_nonslots_these(): """ Enhancing a non-slots class using 'these' works. This will actually *replace* the class with another one, using slots. """ class SimpleOrdinaryClass(object): def __init__(self, x, y, z): self.x = x self.y = y self.z = z def method(self): return self.x @classmethod def classmethod(cls): return "clsmethod" @staticmethod def staticmethod(): return "staticmethod" C2Slots = attr.s(these={"x": attr.ib(), "y": attr.ib(), "z": attr.ib()}, init=False, slots=True)(SimpleOrdinaryClass) c2 = C2Slots(x=1, y=2, z="test") assert 1 == c2.x assert 2 == c2.y assert "test" == c2.z with pytest.raises(AttributeError): c2.t = "test" # We have slots now. assert 1 == c2.method() assert "clsmethod" == c2.classmethod() assert "staticmethod" == c2.staticmethod() assert set(["x", "y", "z"]) == set(C2Slots.__slots__) c3 = C2Slots(x=1, y=3, z="test") assert c3 > c2 c2_ = C2Slots(x=1, y=2, z="test") assert c2 == c2_ assert "SimpleOrdinaryClass(x=1, y=2, z='test')" == repr(c2) hash(c2) # Just to assert it doesn't raise. assert {"x": 1, "y": 2, "z": "test"} == attr.asdict(c2)
def test_adds_all_by_default(self, method_name): """ If no further arguments are supplied, all add_XXX functions except add_hash are applied. __hash__ is set to None. """ # Set the method name to a sentinel and check whether it has been # overwritten afterwards. sentinel = object() class C(object): x = attr.ib() setattr(C, method_name, sentinel) C = attr.s(C) meth = getattr(C, method_name) assert sentinel != meth if method_name == "__hash__": assert meth is None
def test_respects_add_arguments(self, arg_name, method_name): """ If a certain `add_XXX` is `False`, `__XXX__` is not added to the class. """ # Set the method name to a sentinel and check whether it has been # overwritten afterwards. sentinel = object() am_args = { "repr": True, "cmp": True, "hash": True, "init": True } am_args[arg_name] = False class C(object): x = attr.ib() setattr(C, method_name, sentinel) C = attr.s(**am_args)(C) assert sentinel == getattr(C, method_name)
import attr class A(object): a = attr.ib() A = attr.s(A) A(a="test")
def __new__(cls,name,bases,dct): c = super().__new__(cls,name,bases,dct) return attr.s(auto_attribs=True)(c)
def wrap(cl): _add_inflate(cl) _add_validate(cl) _add_serialize(cl) return attr.s(cl)