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
andtarget
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 insource
andtarget
- All attributes in
force_differents
are different insource
andtarget
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',)) True >>> validate_attrs(source, target, force_equals=('age',)) False
If both
force_equals
andforce_differents
are empty it will return True:>>> validate_attrs(source, target) True
- All attributes in
-
xotl.tools.objects.
iterate_over
(source, *keys)[source]¶ Yields pairs of (key, value) for of all
keys
insource
.If any
key
is missing fromsource
is ignored (not yielded).If
source
is acollection
, 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 callingnext
.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 objectobj
, otherwise it returns a partial ofgetattr
onobj
.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
.
-
xotl.tools.objects.
smart_getter_and_deleter
(obj)[source]¶ Returns a function that get and deletes either a key or an attribute of obj depending on the type of
obj
.If
obj
is acollections.Mapping
it must be acollections.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.Examples:
>>> class Foo: ... a = 1 >>> foo = Foo() >>> foo.a = 2 >>> popattr(foo, 'a') 2 >>> popattr(foo, 'a') 1 >>> popattr(foo, 'a') is None True
-
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') 'bar' >>> inst.foo 'bar' >>> inst.spam = 'egg' >>> setdefaultattr(inst, 'spam', 'with ham') 'egg'
-
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
.Parameters: - 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 theattr
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.- meta – If None, the
-
class
xotl.tools.objects.
classproperty
[source]¶ A descriptor that behaves like property for instances but for classes.
Example of its use:
class Foobar: @classproperty 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
toproperty
(i.e same aslambda *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:
modulename.ClassName
Example:
celery.concurrency.processes.TaskPool ^- class name
or using ‘:’ to separate module and symbol:
celery.concurrency.processes:TaskPool
Examples:
>>> 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 True
-
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
insource
that matchespred
(if given).Both
source
andkeys
has the same meaning as initerate_over()
.Parameters: - 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.- default – A value to be returned if no key is found in
-
xotl.tools.objects.
xdir
(obj, filter=None, attr_filter=None, value_filter=None, getattr=None)[source]¶ Return all
(attr, value)
pairs fromobj
makefilter(attr, value)
True.Parameters: - 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 fromobj
. If None, usegetattr()
.
Changed in version 1.8.1: Removed deprecated
attr_filter
andvalue_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
totarget
.Parameters: - 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
andtarget
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 overdefaults
will be copied. Ifdefaults
is a dictionary, and one of its key is not found in any of thesources
, then the value of the key in the dictionary is copied totarget
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 totarget
.If
defaults
is a callable then it should receive one positional arguments for the currentattribute name
and several keyword arguments (we passsource
) 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 ofcollections.Mapping
or aMappingProxyType
.The
target
is considered a mapping if it’s an instance ofcollections.MutableMapping
.Returns: target
.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 theobj
; otherwise the names are considered regular attribute names.If
default
is Unset and any name is not found, an AttributeError is raised, otherwise thedefault
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 inget_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 isUnset
) 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 ansmart_getter()
over the objects. If providedgetter
should have the signature ofgetattr()
.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 bothoperator.itemgetter()
andoperator.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 eachpath
.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
orsets
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.
- Any two
-
xotl.tools.objects.
pop_first_of
(source, *keys, default=None)[source]¶ Similar to
get_first_of()
using assource
either an object or a mapping and deleting the first attribute or key.Examples:
>>> 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') 'eggs-dict' >>> pop_first_of((somedict, foo), 'eggs') 'eggs-obj' >>> pop_first_of((somedict, foo), 'eggs') is None True >>> pop_first_of((foo, somedict), 'bar') 'bar-obj' >>> pop_first_of((foo, somedict), 'bar') 'bar-dict' >>> pop_first_of((foo, somedict), 'bar') is None True
-
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.Parameters: - 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 givenids
.Parameters: - 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 ifsource
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.
- a string: is considered a key, if
Example:
>>> 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')), '---') 2 >>> next(multi_getter(d, 'a', ('b', 'c'), ('e', 'f')), '---') is None True
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] True
You can set
without_duplicates
to True to avoid this:>>> get_branch_subclasses(Foo, without_duplicates=True) == [Final] True
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 includingsuperclass
).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 withsmart_getter()
andsmart_setter()
. You can override passing keywordgetter
andsetter
. 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 1 >>> obj.b 3
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 True
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
andsetter
have the same meaning as insave_attributes()
. We also use thesetter
to set the values provided inattrs
.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: http://www.opensource.org/licenses/mit-license.php
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 inattrs_map
.Parameters: - 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.
Example:
>>> class Bar: ... x = 'bar'
>>> class Foo(delegator('egg', {'x1': 'x'})): ... def __init__(self): ... self.egg = Bar()
>>> foo = Foo() >>> foo.x1 'bar'
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 attributetarget_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
anddefault
isUnset
, raise an AttributeError. Ifdefault
is not Unset, returndefault
.New in version 1.9.3.