mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 15:56:07 -07:00
Add jaraco.classes-3.2.1
This commit is contained in:
parent
182e5f553e
commit
fed42da9bb
4 changed files with 305 additions and 0 deletions
68
lib/jaraco/classes/ancestry.py
Normal file
68
lib/jaraco/classes/ancestry.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
"""
|
||||||
|
Routines for obtaining the class names
|
||||||
|
of an object and its parent classes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from more_itertools import unique_everseen
|
||||||
|
|
||||||
|
|
||||||
|
def all_bases(c):
|
||||||
|
"""
|
||||||
|
return a tuple of all base classes the class c has as a parent.
|
||||||
|
>>> object in all_bases(list)
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
return c.mro()[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def all_classes(c):
|
||||||
|
"""
|
||||||
|
return a tuple of all classes to which c belongs
|
||||||
|
>>> list in all_classes(list)
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
return c.mro()
|
||||||
|
|
||||||
|
|
||||||
|
# borrowed from
|
||||||
|
# http://code.activestate.com/recipes/576949-find-all-subclasses-of-a-given-class/
|
||||||
|
|
||||||
|
|
||||||
|
def iter_subclasses(cls):
|
||||||
|
"""
|
||||||
|
Generator over all subclasses of a given class, in depth-first order.
|
||||||
|
|
||||||
|
>>> bool in list(iter_subclasses(int))
|
||||||
|
True
|
||||||
|
>>> class A(object): pass
|
||||||
|
>>> class B(A): pass
|
||||||
|
>>> class C(A): pass
|
||||||
|
>>> class D(B,C): pass
|
||||||
|
>>> class E(D): pass
|
||||||
|
>>>
|
||||||
|
>>> for cls in iter_subclasses(A):
|
||||||
|
... print(cls.__name__)
|
||||||
|
B
|
||||||
|
D
|
||||||
|
E
|
||||||
|
C
|
||||||
|
>>> # get ALL classes currently defined
|
||||||
|
>>> res = [cls.__name__ for cls in iter_subclasses(object)]
|
||||||
|
>>> 'type' in res
|
||||||
|
True
|
||||||
|
>>> 'tuple' in res
|
||||||
|
True
|
||||||
|
>>> len(res) > 100
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
return unique_everseen(_iter_all_subclasses(cls))
|
||||||
|
|
||||||
|
|
||||||
|
def _iter_all_subclasses(cls):
|
||||||
|
try:
|
||||||
|
subs = cls.__subclasses__()
|
||||||
|
except TypeError: # fails only when cls is type
|
||||||
|
subs = cls.__subclasses__(cls)
|
||||||
|
for sub in subs:
|
||||||
|
yield sub
|
||||||
|
yield from iter_subclasses(sub)
|
66
lib/jaraco/classes/meta.py
Normal file
66
lib/jaraco/classes/meta.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
"""
|
||||||
|
meta.py
|
||||||
|
|
||||||
|
Some useful metaclasses.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class LeafClassesMeta(type):
|
||||||
|
"""
|
||||||
|
A metaclass for classes that keeps track of all of them that
|
||||||
|
aren't base classes.
|
||||||
|
|
||||||
|
>>> Parent = LeafClassesMeta('MyParentClass', (), {})
|
||||||
|
>>> Parent in Parent._leaf_classes
|
||||||
|
True
|
||||||
|
>>> Child = LeafClassesMeta('MyChildClass', (Parent,), {})
|
||||||
|
>>> Child in Parent._leaf_classes
|
||||||
|
True
|
||||||
|
>>> Parent in Parent._leaf_classes
|
||||||
|
False
|
||||||
|
|
||||||
|
>>> Other = LeafClassesMeta('OtherClass', (), {})
|
||||||
|
>>> Parent in Other._leaf_classes
|
||||||
|
False
|
||||||
|
>>> len(Other._leaf_classes)
|
||||||
|
1
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(cls, name, bases, attrs):
|
||||||
|
if not hasattr(cls, '_leaf_classes'):
|
||||||
|
cls._leaf_classes = set()
|
||||||
|
leaf_classes = getattr(cls, '_leaf_classes')
|
||||||
|
leaf_classes.add(cls)
|
||||||
|
# remove any base classes
|
||||||
|
leaf_classes -= set(bases)
|
||||||
|
|
||||||
|
|
||||||
|
class TagRegistered(type):
|
||||||
|
"""
|
||||||
|
As classes of this metaclass are created, they keep a registry in the
|
||||||
|
base class of all classes by a class attribute, indicated by attr_name.
|
||||||
|
|
||||||
|
>>> FooObject = TagRegistered('FooObject', (), dict(tag='foo'))
|
||||||
|
>>> FooObject._registry['foo'] is FooObject
|
||||||
|
True
|
||||||
|
>>> BarObject = TagRegistered('Barobject', (FooObject,), dict(tag='bar'))
|
||||||
|
>>> FooObject._registry is BarObject._registry
|
||||||
|
True
|
||||||
|
>>> len(FooObject._registry)
|
||||||
|
2
|
||||||
|
|
||||||
|
'...' below should be 'jaraco.classes' but for pytest-dev/pytest#3396
|
||||||
|
>>> FooObject._registry['bar']
|
||||||
|
<class '....meta.Barobject'>
|
||||||
|
"""
|
||||||
|
|
||||||
|
attr_name = 'tag'
|
||||||
|
|
||||||
|
def __init__(cls, name, bases, namespace):
|
||||||
|
super(TagRegistered, cls).__init__(name, bases, namespace)
|
||||||
|
if not hasattr(cls, '_registry'):
|
||||||
|
cls._registry = {}
|
||||||
|
meta = cls.__class__
|
||||||
|
attr = getattr(cls, meta.attr_name, None)
|
||||||
|
if attr:
|
||||||
|
cls._registry[attr] = cls
|
171
lib/jaraco/classes/properties.py
Normal file
171
lib/jaraco/classes/properties.py
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
class NonDataProperty:
|
||||||
|
"""Much like the property builtin, but only implements __get__,
|
||||||
|
making it a non-data property, and can be subsequently reset.
|
||||||
|
|
||||||
|
See http://users.rcn.com/python/download/Descriptor.htm for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
>>> class X(object):
|
||||||
|
... @NonDataProperty
|
||||||
|
... def foo(self):
|
||||||
|
... return 3
|
||||||
|
>>> x = X()
|
||||||
|
>>> x.foo
|
||||||
|
3
|
||||||
|
>>> x.foo = 4
|
||||||
|
>>> x.foo
|
||||||
|
4
|
||||||
|
|
||||||
|
'...' below should be 'jaraco.classes' but for pytest-dev/pytest#3396
|
||||||
|
>>> X.foo
|
||||||
|
<....properties.NonDataProperty object at ...>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, fget):
|
||||||
|
assert fget is not None, "fget cannot be none"
|
||||||
|
assert callable(fget), "fget must be callable"
|
||||||
|
self.fget = fget
|
||||||
|
|
||||||
|
def __get__(self, obj, objtype=None):
|
||||||
|
if obj is None:
|
||||||
|
return self
|
||||||
|
return self.fget(obj)
|
||||||
|
|
||||||
|
|
||||||
|
class classproperty:
|
||||||
|
"""
|
||||||
|
Like @property but applies at the class level.
|
||||||
|
|
||||||
|
|
||||||
|
>>> class X(metaclass=classproperty.Meta):
|
||||||
|
... val = None
|
||||||
|
... @classproperty
|
||||||
|
... def foo(cls):
|
||||||
|
... return cls.val
|
||||||
|
... @foo.setter
|
||||||
|
... def foo(cls, val):
|
||||||
|
... cls.val = val
|
||||||
|
>>> X.foo
|
||||||
|
>>> X.foo = 3
|
||||||
|
>>> X.foo
|
||||||
|
3
|
||||||
|
>>> x = X()
|
||||||
|
>>> x.foo
|
||||||
|
3
|
||||||
|
>>> X.foo = 4
|
||||||
|
>>> x.foo
|
||||||
|
4
|
||||||
|
|
||||||
|
Setting the property on an instance affects the class.
|
||||||
|
|
||||||
|
>>> x.foo = 5
|
||||||
|
>>> x.foo
|
||||||
|
5
|
||||||
|
>>> X.foo
|
||||||
|
5
|
||||||
|
>>> vars(x)
|
||||||
|
{}
|
||||||
|
>>> X().foo
|
||||||
|
5
|
||||||
|
|
||||||
|
Attempting to set an attribute where no setter was defined
|
||||||
|
results in an AttributeError:
|
||||||
|
|
||||||
|
>>> class GetOnly(metaclass=classproperty.Meta):
|
||||||
|
... @classproperty
|
||||||
|
... def foo(cls):
|
||||||
|
... return 'bar'
|
||||||
|
>>> GetOnly.foo = 3
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AttributeError: can't set attribute
|
||||||
|
|
||||||
|
It is also possible to wrap a classmethod or staticmethod in
|
||||||
|
a classproperty.
|
||||||
|
|
||||||
|
>>> class Static(metaclass=classproperty.Meta):
|
||||||
|
... @classproperty
|
||||||
|
... @classmethod
|
||||||
|
... def foo(cls):
|
||||||
|
... return 'foo'
|
||||||
|
... @classproperty
|
||||||
|
... @staticmethod
|
||||||
|
... def bar():
|
||||||
|
... return 'bar'
|
||||||
|
>>> Static.foo
|
||||||
|
'foo'
|
||||||
|
>>> Static.bar
|
||||||
|
'bar'
|
||||||
|
|
||||||
|
*Legacy*
|
||||||
|
|
||||||
|
For compatibility, if the metaclass isn't specified, the
|
||||||
|
legacy behavior will be invoked.
|
||||||
|
|
||||||
|
>>> class X:
|
||||||
|
... val = None
|
||||||
|
... @classproperty
|
||||||
|
... def foo(cls):
|
||||||
|
... return cls.val
|
||||||
|
... @foo.setter
|
||||||
|
... def foo(cls, val):
|
||||||
|
... cls.val = val
|
||||||
|
>>> X.foo
|
||||||
|
>>> X.foo = 3
|
||||||
|
>>> X.foo
|
||||||
|
3
|
||||||
|
>>> x = X()
|
||||||
|
>>> x.foo
|
||||||
|
3
|
||||||
|
>>> X.foo = 4
|
||||||
|
>>> x.foo
|
||||||
|
4
|
||||||
|
|
||||||
|
Note, because the metaclass was not specified, setting
|
||||||
|
a value on an instance does not have the intended effect.
|
||||||
|
|
||||||
|
>>> x.foo = 5
|
||||||
|
>>> x.foo
|
||||||
|
5
|
||||||
|
>>> X.foo # should be 5
|
||||||
|
4
|
||||||
|
>>> vars(x) # should be empty
|
||||||
|
{'foo': 5}
|
||||||
|
>>> X().foo # should be 5
|
||||||
|
4
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta(type):
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
obj = self.__dict__.get(key, None)
|
||||||
|
if type(obj) is classproperty:
|
||||||
|
return obj.__set__(self, value)
|
||||||
|
return super().__setattr__(key, value)
|
||||||
|
|
||||||
|
def __init__(self, fget, fset=None):
|
||||||
|
self.fget = self._fix_function(fget)
|
||||||
|
self.fset = fset
|
||||||
|
fset and self.setter(fset)
|
||||||
|
|
||||||
|
def __get__(self, instance, owner=None):
|
||||||
|
return self.fget.__get__(None, owner)()
|
||||||
|
|
||||||
|
def __set__(self, owner, value):
|
||||||
|
if not self.fset:
|
||||||
|
raise AttributeError("can't set attribute")
|
||||||
|
if type(owner) is not classproperty.Meta:
|
||||||
|
owner = type(owner)
|
||||||
|
return self.fset.__get__(None, owner)(value)
|
||||||
|
|
||||||
|
def setter(self, fset):
|
||||||
|
self.fset = self._fix_function(fset)
|
||||||
|
return self
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _fix_function(cls, fn):
|
||||||
|
"""
|
||||||
|
Ensure fn is a classmethod or staticmethod.
|
||||||
|
"""
|
||||||
|
if not isinstance(fn, (classmethod, staticmethod)):
|
||||||
|
return classmethod(fn)
|
||||||
|
return fn
|
Loading…
Add table
Add a link
Reference in a new issue