xotl.tools.objects - Functions for dealing with objects

Several utilities for objects in general.

xotl.tools.objects.validate_attrs(source, target, force_equals=(), force_differents=())[source]

Makes a ‘comparison’ of source and target by its attributes (or keys).

This function returns True if and only if both of these tests pass:

  • All attributes in force_equals are equal in source and target
  • All attributes in force_differents are different in source and target

For instance:

>>> class Person:
...    def __init__(self, **kwargs):
...        for which in kwargs:
...            setattr(self, which, kwargs[which])

>>> source = Person(name='Manuel', age=33, sex='male')
>>> target = {'name': 'Manuel', 'age': 4, 'sex': 'male'}

>>> validate_attrs(source, target, force_equals=('sex',),
...                force_differents=('age',))

>>> validate_attrs(source, target, force_equals=('age',))

If both force_equals and force_differents are empty it will return True:

>>> validate_attrs(source, target)
xotl.tools.objects.iterate_over(source, *keys)[source]

Yields pairs of (key, value) for of all keys in source.

If any key is missing from source is ignored (not yielded).

If source is a collection, iterate over each of the items searching for any of keys. This is not recursive.

If no keys are provided, return an “empty” iterator – i.e will raise StopIteration upon calling next.

New in version 1.5.2.

xotl.tools.objects.smart_getter(obj, strict=False)[source]

Returns a smart getter for obj.

If obj is a mapping, it returns the .get() method bound to the object obj, otherwise it returns a partial of getattr on obj.

Parameters:strict – Set this to True so that the returned getter checks that keys/attrs exists. If strict is True the getter may raise a KeyError or an AttributeError.

Changed in version 1.5.3: Added the parameter strict.


Returns a function that get and deletes either a key or an attribute of obj depending on the type of obj.

If obj is a collections.Mapping it must be a collections.MutableMapping.

xotl.tools.objects.popattr(obj, name, default=None)[source]

Looks for an attribute in the obj and returns its value and removes the attribute. If the attribute is not found, default is returned instead.


>>> class Foo:
...   a = 1
>>> foo = Foo()
>>> foo.a = 2
>>> popattr(foo, 'a')
>>> popattr(foo, 'a')
>>> popattr(foo, 'a') is None
xotl.tools.objects.setdefaultattr(obj, name, value)[source]

Sets the attribute name to value if it is not set:

>>> class Someclass: pass
>>> inst = Someclass()
>>> setdefaultattr(inst, 'foo', 'bar')

>>> inst.foo

>>> inst.spam = 'egg'
>>> setdefaultattr(inst, 'spam', 'with ham')
xotl.tools.objects.copy_class(cls, meta=None, ignores=None, new_attrs=None, new_name=None)[source]

Copies a class definition to a new class.

The returned class will have the same name, bases and module of cls.

  • meta – If None, the type(cls) of the class is used to build the new class, otherwise this must be a proper metaclass.
  • ignores

    A sequence of attributes names that should not be copied to the new class.

    An item may be callable accepting a single argument attr that must return a non-null value if the the attr should be ignored.

  • new_attrs (dict) – New attributes the class must have. These will take precedence over the attributes in the original class.
  • new_name – The name for the copy. If not provided the name will copied.

New in version 1.4.0.

Changed in version 1.7.1: The ignores argument must an iterable of strings or callables. Removed the glob-pattern and regular expressions as possible values. They are all possible via the callable variant.

New in version 1.7.1: The new_name argument.


Return a set with all attribute names defined in obj

class xotl.tools.objects.classproperty[source]

A descriptor that behaves like property for instances but for classes.

Example of its use:

class Foobar:
    def getx(cls):
        return cls._x

A writable classproperty is difficult to define, and it’s not intended for that case because ‘setter’, and ‘deleter’ decorators can’t be used for obvious reasons. For example:

class Foobar:
    x = 1
    def __init__(self, x=2):
        self.x = x
    def _get_name(cls):
        return str(cls.x)
    def _set_name(cls, x):
        cls.x = int(x)
    name = classproperty(_get_name, _set_name)

In Python 3.9+ this is actually the composition of classmethod to property (i.e same as lambda *a, **kw: classmethod(property(*a, **kw))).

New in version 1.4.1.

Changed in version 1.8.0: Inherits from property

Changed in version 2.11.0: Changed to be compose(classmethod, property) in Python 3.9+.

xotl.tools.objects.import_object(name, package=None, sep='.', default=None, **kwargs)[source]

Get symbol by qualified name.

The name should be the full dot-separated path to the class:



                            ^- class name

or using ‘:’ to separate module and symbol:



>>> import_object('celery.concurrency.processes.TaskPool')
<class 'celery.concurrency.processes.TaskPool'>

# Does not try to look up non-string names.
>>> from celery.concurrency.processes import TaskPool
>>> import_object(TaskPool) is TaskPool
xotl.tools.objects.get_first_of(sources, *keys, default=None, pred=None)[source]

Return the value of the first occurrence of any of the specified keys in source that matches pred (if given).

Both source and keys has the same meaning as in iterate_over().

  • default – A value to be returned if no key is found in source.
  • pred – A function that should receive a single value and return False if the value is not acceptable, and thus get_first_of should look for another.

Changed in version 1.5.2: Added the pred option.

xotl.tools.objects.xdir(obj, filter=None, attr_filter=None, value_filter=None, getattr=None)[source]

Return all (attr, value) pairs from obj make filter(attr, value) True.

  • obj – The object to be instrospected.
  • filter

    A filter that will be passed both the attribute name and it’s value as two positional arguments. It should return True for attrs that should be yielded.

    If None, all pairs will match.

  • getter – A function with the same signature that getattr to be used to get the values from obj. If None, use getattr().

Changed in version 1.8.1: Removed deprecated attr_filter and value_filter arguments.

xotl.tools.objects.fdir(obj, filter=None, attr_filter=None, value_filter=None, getattr=None)[source]

Similar to xdir() but yields only the attributes names.

xotl.tools.objects.smart_copy(*sources, target, *, defaults=False)[source]

Copies the first apparition of attributes (or keys) from sources to target.

  • sources – The objects from which to extract keys or attributes.
  • target – The object to fill.
  • defaults (Either a bool, a dictionary, an iterable or a callable.) – Default values for the attributes to be copied as explained below. Defaults to False.

Every sources and target are always positional arguments. There should be at least one source. target will always be the last positional argument.

If defaults is a dictionary or an iterable then only the names provided by itering over defaults will be copied. If defaults is a dictionary, and one of its key is not found in any of the sources, then the value of the key in the dictionary is copied to target unless:

  • It’s the value Undefined.
  • An exception object
  • A sequence with is first value being a subclass of Exception. In which case adapt_exception is used.

In these cases a KeyError is raised if the key is not found in the sources.

If defaults is an iterable and a key is not found in any of the sources, None is copied to target.

If defaults is a callable then it should receive one positional arguments for the current attribute name and several keyword arguments (we pass source) and return either True or False if the attribute should be copied.

If defaults is False (or None) only the attributes that do not start with a “_” are copied, if it’s True all attributes are copied.

When target is not a mapping only valid Python identifiers will be copied.

Each source is considered a mapping if it’s an instance of collections.Mapping or a MappingProxyType.

The target is considered a mapping if it’s an instance of collections.MutableMapping.


Changed in version 1.7.0: defaults is now keyword only.

xotl.tools.objects.extract_attrs(obj, *names, default=Unset)[source]

Extracts all names from an object.

If obj is a Mapping, the names will be search in the keys of the obj; otherwise the names are considered regular attribute names.

If default is Unset and any name is not found, an AttributeError is raised, otherwise the default is used instead.

Returns a tuple if there are more that one name, otherwise returns a single value.

New in version 1.4.0.

Changed in version 1.5.3: Each name may be a path like in get_traverser(), but only “.” is allowed as separator.

xotl.tools.objects.traverse(obj, path, default=Unset, sep='.', getter=None)[source]

Traverses an object’s hierarchy by performing an attribute get at each level.

This helps getting an attribute that is buried down several levels deep. For example:

traverse(request, 'session.somevalue')

If default is not provided (i.e is Unset) and any component in the path is not found an AttributeError exceptions is raised.

You may provide sep to change the default separator.

You may provide a custom getter. By default, does an smart_getter() over the objects. If provided getter should have the signature of getattr().

See get_traverser() if you need to apply the same path(s) to several objects. Actually this is equivalent to:

get_traverser(path, default=default, sep=sep, getter=getter)(obj)
xotl.tools.objects.get_traverser(*paths, default=Unset, sep='.', getter=None)[source]

Combines the power of traverse() with the expectations from both operator.itemgetter() and operator.attrgetter().

Parameters:paths – Several paths to extract.

Keyword arguments has the same meaning as in traverse().

Returns:A function the when invoked with an object traverse the object finding each path.

New in version 1.5.3.

xotl.tools.objects.dict_merge(*dicts, **other)[source]

Merges several dicts into a single one.

Merging is similar to updating a dict, but if values are non-scalars they are also merged is this way:

  • Any two sequences or sets are joined together.
  • Any two mappings are recursively merged.
  • Other types are just replaced like in update().

If for a single key two values of incompatible types are found, raise a TypeError. If the values for a single key are compatible but different (i.e a list an a tuple) the resultant type will be the type of the first apparition of the key, unless for mappings which are always cast to dicts.

No matter the types of dicts the result is always a dict.

Without arguments, return the empty dict.

xotl.tools.objects.pop_first_of(source, *keys, default=None)[source]

Similar to get_first_of() using as source either an object or a mapping and deleting the first attribute or key.


>>> somedict = dict(bar='bar-dict', eggs='eggs-dict')

>>> class Foo: pass
>>> foo = Foo()
>>> foo.bar = 'bar-obj'
>>> foo.eggs = 'eggs-obj'

>>> pop_first_of((somedict, foo), 'eggs')

>>> pop_first_of((somedict, foo), 'eggs')

>>> pop_first_of((somedict, foo), 'eggs') is None

>>> pop_first_of((foo, somedict), 'bar')

>>> pop_first_of((foo, somedict), 'bar')

>>> pop_first_of((foo, somedict), 'bar') is None
xotl.tools.objects.fix_method_documentation(cls, method_name, ignore=None, min_length=10, deep=1, default=None)[source]

Fix the documentation for the given class using its super-classes.

This function may be useful for shells or Python Command Line Interfaces (CLI).

If cls has an invalid documentation, super-classes are recursed in MRO until a documentation definition was made at any level.

  • ignore – could be used to specify which classes to ignore by specifying its name in this list.
  • min_length – specify that documentations with less that a number of characters, also are ignored.
xotl.tools.objects.multi_getter(source, *ids)[source]

Get values from source of all given ids.

  • source – Any object but dealing with differences between mappings and other object types.
  • ids

    Identifiers to get values from source.

    An ID item could be:

    • a string: is considered a key, if source is a mapping, or an attribute name if source is an instance of any other type.
    • a collection of strings: find the first valid value in source evaluating each item in this collection using the above logic.


>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> list(multi_getter(d, 'a', ('y', 'x'), ('x', 'y'), ('a', 'z', 'x')))
[None, 2, 1, 3]

>>> next(multi_getter(d, ('y', 'x'), ('x', 'y')), '---')

>>> next(multi_getter(d, 'a', ('b', 'c'), ('e', 'f')), '---') is None

New in version 1.7.1.

xotl.tools.objects.get_branch_subclasses(cls, *, include_this=False, without_duplicates=False)[source]

Similar to type.__subclasses__() but recursive.

Only return sub-classes in branches (those with no sub-classes). Return a list of all classes reachable from cls.

The same class can be included more than once if there are two different paths from cls to that class:

>>> class Foo: pass
>>> class Bar(Foo): pass
>>> class Baz(Foo): pass
>>> class Final(Bar, Baz): pass

>>> get_branch_subclasses(Foo) == [Final, Final]

You can set without_duplicates to True to avoid this:

>>> get_branch_subclasses(Foo, without_duplicates=True) == [Final]

New in version 1.7.0.

Changed in version 2.1.5: Add keyword-only argument include_this.

Changed in version 2.2.5: Add keyword-only argument without_duplicates.

xotl.tools.objects.iter_branch_subclasses(cls, include_this=True, without_duplicates=False)[source]

Internal function, see get_branch_subclasses().

xotl.tools.objects.FinalSubclassEnumeration(superclass, *, dynamic=True)[source]

A final sub-class enumeration.

Return a enumeration-like class (i.e has __members__ and each attribute) that enumerates the final subclasses of a given superclass (not including superclass).

If dynamic is True, don’t cache the subclasses; i.e if a new subclass is created after the enumeration, the __members__ dictionary will change.

The resulting enumeration class has a method invalidate_cache() which allows non-dynamic classes to update its underlying cache.

New in version 2.1.0.

xotl.tools.objects.save_attributes(obj, *attributes, getter=None, setter=None)[source]

A context manager that restores obj attributes at exit.

We deal with obj’s attributes with smart_getter() and smart_setter(). You can override passing keyword getter and setter. They must take the object and return a callable to get/set the its attributes.

Basic example:

>>> from xotl.tools.future.types import SimpleNamespace as new
>>> obj = new(a=1, b=2)

>>> with save_attributes(obj, 'a'):
...    obj.a = 2
...    obj.b = 3

>>> obj.a

>>> obj.b

Depending on the behavior of getter and or the object itself, it may be an error to get an attribute or key that does not exists.

>>> getter = lambda o: lambda a: getattr(o, a)
>>> with save_attributes(obj, 'c', getter=getter):   
...    pass
Traceback (...)
AttributeError: ...

Beware, however, that smart_getter() is non-strict by default and it returns None for a non-existing key or attribute. In this case, we attempt to set that attribute or key at exit:

>>> with save_attributes(obj, 'x'):
...   pass

>>> obj.x is None

But, then, setting the value may fail:

>>> obj = object()
>>> with save_attribute(obj, 'x'):  
...   pass
Traceback (...)
AttributeError: ...

New in version 1.8.2.

xotl.tools.objects.temp_attributes(obj, attrs, getter=None, setter=None)[source]

A context manager that temporarily sets attributes.

attrs is a dictionary containing the attributes to set.

Keyword arguments getter and setter have the same meaning as in save_attributes(). We also use the setter to set the values provided in attrs.

New in version 1.8.5.

class xotl.tools.objects.memoized_property[source]

A read-only property that is only evaluated once.

In Python 3.8+ this is based on functools.cached_property.

In Python version before 3.8, this code is extracted from the SQLAlchemy project’s codebase, merit and copyright goes to SQLAlchemy authors:

Copyright (C) 2005-2011 the SQLAlchemy authors and contributors

This module is part of SQLAlchemy and is released under the MIT License:

New in version 1.8.1: Ported from xoutil.decorator.memoized_property.

New in version 2.11.0: Subclass of functools.cached_property in Python 3.8+.

xotl.tools.objects.delegator(attribute, attrs_map, metaclass=<class 'type'>)[source]

Create a base class that delegates attributes to another object.

The returned base class contains a delegated attribute descriptor for each key in attrs_map.

  • attribute – The attribute of the delegating object that holds the delegated attributes.
  • attrs_map – A map of attributes to delegate. The keys are the attribute names the delegating object attributes, and the values the attribute names of the delegated object.


>>> class Bar:
...     x = 'bar'
>>> class Foo(delegator('egg', {'x1': 'x'})):
...     def __init__(self):
...         self.egg = Bar()
>>> foo = Foo()
>>> foo.x1

New in version 1.9.3.

class xotl.tools.objects.DelegatedAttribute(target_name, delegated_attr, default=Unset)[source]

A delegator data descriptor.

When accessed the descriptor finds the delegated_attr in the instance’s value given by attribute target_name.

If the instance has no attribute with name target_name, raise an AttributeError.

If the target object does not have an attribute with name delegate_attr and default is Unset, raise an AttributeError. If default is not Unset, return default.

New in version 1.9.3.