Use objtools.registry
This commit is contained in:
parent
e75fb71581
commit
d99a9b36ef
|
@ -0,0 +1 @@
|
|||
objtools>=0.1
|
|
@ -1,24 +1,32 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from stdqs.utils import Registry
|
||||
from objtools.registry import ClassRegistry
|
||||
import operator
|
||||
|
||||
|
||||
class LookupMetaclass(ABCMeta):
|
||||
class LookupRegistry(ClassRegistry):
|
||||
|
||||
def __new__(cls, name, *args, **kwargs):
|
||||
newclass = ABCMeta.__new__(cls, name, *args, **kwargs)
|
||||
if name != "Lookup":
|
||||
register(newclass)
|
||||
return newclass
|
||||
def check_key(self, key):
|
||||
assert isinstance(key, str), 'Lookup name must be a string.'
|
||||
|
||||
def check_value(self, value):
|
||||
assert issubclass(value, Lookup), \
|
||||
'Lookup should inherit from the Lookup abstract class.'
|
||||
|
||||
|
||||
class Lookup(metaclass=LookupMetaclass):
|
||||
registry = LookupRegistry()
|
||||
register = registry.register
|
||||
unregister = registry.unregister
|
||||
|
||||
|
||||
class LookupMetaclass(registry.metaclass, ABCMeta):
|
||||
pass
|
||||
|
||||
|
||||
class Lookup(metaclass=LookupMetaclass, register=False):
|
||||
"""
|
||||
A filtering operation: The "lt" in `field__lt=4`.
|
||||
"""
|
||||
|
||||
lookup_name = None
|
||||
|
||||
def __init__(self, lhs, rhs):
|
||||
self.lhs, self.rhs = lhs, rhs
|
||||
|
||||
|
@ -55,54 +63,12 @@ def func_lookup(func, name=None):
|
|||
{
|
||||
'__module__': func_lookup.__module__,
|
||||
'__qualname__': class_name,
|
||||
'lookup_name': name,
|
||||
'filter_obj': lambda self, obj: func(obj, self.rhs)
|
||||
}
|
||||
},
|
||||
key=name,
|
||||
)
|
||||
|
||||
|
||||
class LookupRegistry(Registry):
|
||||
|
||||
def check_key(self, key):
|
||||
assert isinstance(key, str), 'Lookup name must be a string.'
|
||||
|
||||
def check_value(self, value):
|
||||
assert callable(getattr(value, 'filter_obj', None)), \
|
||||
'{} should implement the filter_obj method.'.format(repr(value))
|
||||
|
||||
def register(self, key, value=None):
|
||||
"""
|
||||
Register a lookup.
|
||||
May be used as register(lookup_class) if the lookup has a defined name,
|
||||
or as register(name, lookup_class) or register(lookup_class, name)
|
||||
to set a custom name.
|
||||
Can be used as a decorator on a Lookup if it has a defined name.
|
||||
"""
|
||||
if value:
|
||||
try:
|
||||
return super().register(key, value)
|
||||
except (KeyError, ValueError):
|
||||
# Try the other way around
|
||||
try:
|
||||
return super().register(value, key)
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
raise
|
||||
|
||||
if not getattr(key, 'lookup_name', None):
|
||||
raise KeyError(
|
||||
'Lookup is missing a lookup name.'
|
||||
'Set the lookup_name attribute or use register(name, lookup).'
|
||||
)
|
||||
|
||||
super().register(key.lookup_name, key)
|
||||
|
||||
|
||||
registry = LookupRegistry()
|
||||
register = registry.register
|
||||
unregister = registry.unregister
|
||||
|
||||
|
||||
LessThanLookup = func_lookup(operator.lt)
|
||||
GreaterThanLookup = func_lookup(operator.gt)
|
||||
LessOrEqualLookup = func_lookup(operator.le, name='lte')
|
||||
|
|
|
@ -1,18 +1,25 @@
|
|||
from stdqs.parsing.lookups import Lookup, LookupMetaclass, LookupRegistry
|
||||
from stdqs.parsing.lookups import Lookup, LookupRegistry
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from collections.abc import Iterator
|
||||
|
||||
|
||||
class TransformMetaclass(LookupMetaclass):
|
||||
class TransformRegistry(LookupRegistry):
|
||||
|
||||
def __new__(cls, name, *args, **kwargs):
|
||||
newclass = ABCMeta.__new__(cls, name, *args, **kwargs)
|
||||
if name not in ("Transform", "GetterTransform"):
|
||||
register(newclass)
|
||||
return newclass
|
||||
def check_value(self, value):
|
||||
assert issubclass(value, Transform), \
|
||||
'Transform must inherit from the Transform base class'
|
||||
|
||||
|
||||
class Transform(Lookup, metaclass=TransformMetaclass):
|
||||
registry = TransformRegistry()
|
||||
register = registry.register
|
||||
unregister = registry.unregister
|
||||
|
||||
|
||||
class TransformMetaclass(registry.metaclass, ABCMeta):
|
||||
pass
|
||||
|
||||
|
||||
class Transform(metaclass=TransformMetaclass, register=False):
|
||||
"""
|
||||
A special kind of lookup that also allows lookups on itself.
|
||||
For example, the "abs" in `field__abs__lt=2`.
|
||||
|
@ -20,7 +27,7 @@ class Transform(Lookup, metaclass=TransformMetaclass):
|
|||
"""
|
||||
|
||||
def __init__(self, lhs, rhs=None):
|
||||
super().__init__(lhs, rhs)
|
||||
self.lhs, self.rhs = lhs, rhs
|
||||
|
||||
def filter_obj(self, obj):
|
||||
transformed = self.transform_obj(obj)
|
||||
|
@ -36,6 +43,13 @@ class Transform(Lookup, metaclass=TransformMetaclass):
|
|||
return NotImplemented.
|
||||
"""
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({!r},{!r})'.format(
|
||||
self.__class__.__name__,
|
||||
self.lhs,
|
||||
self.rhs,
|
||||
)
|
||||
|
||||
|
||||
def func_transform(func, name=None):
|
||||
"""
|
||||
|
@ -54,13 +68,13 @@ def func_transform(func, name=None):
|
|||
{
|
||||
'__module__': func_transform.__module__,
|
||||
'__qualname__': class_name,
|
||||
'lookup_name': name,
|
||||
'transform_obj': lambda self, obj: func(obj)
|
||||
}
|
||||
},
|
||||
key=name,
|
||||
)
|
||||
|
||||
|
||||
class GetterTransform(Transform):
|
||||
class GetterTransform(Transform, register=False):
|
||||
"""
|
||||
A special Transform that fetches values from attributes, keys or methods.
|
||||
Should not be registered as a normal Transform.
|
||||
|
@ -84,18 +98,6 @@ class GetterTransform(Transform):
|
|||
return None
|
||||
|
||||
|
||||
class TransformRegistry(LookupRegistry):
|
||||
|
||||
def check_value(self, value):
|
||||
super().check_value(value)
|
||||
assert callable(getattr(value, 'transform_obj', None)), \
|
||||
'{} should implement the transform_obj method.'.format(repr(value))
|
||||
|
||||
|
||||
registry = TransformRegistry()
|
||||
register = registry.register
|
||||
unregister = registry.unregister
|
||||
|
||||
AbsoluteTransform = func_transform(abs)
|
||||
AsciiTransform = func_transform(ascii)
|
||||
BinaryTransform = func_transform(bin)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from collections.abc import Sequence, Collection, Iterator
|
||||
from objtools.registry import ClassRegistry
|
||||
from stdqs.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||
from stdqs.utils import Registry, flatten
|
||||
from stdqs.utils import flatten
|
||||
from stdqs.parsing import QueryParser
|
||||
|
||||
REPR_OUTPUT_SIZE = 20
|
||||
|
@ -165,7 +166,7 @@ class DictQuerySet(QuerySet):
|
|||
return flatten(self.collection)
|
||||
|
||||
|
||||
class QuerySetRegistry(Registry):
|
||||
class QuerySetRegistry(ClassRegistry):
|
||||
|
||||
def check_key(self, key):
|
||||
assert isinstance(key, type), \
|
||||
|
|
|
@ -1,39 +1,6 @@
|
|||
from collections.abc import Mapping, Iterable
|
||||
|
||||
|
||||
class Registry(dict):
|
||||
|
||||
unregister = dict.__delitem__
|
||||
|
||||
def check_key(self, key):
|
||||
pass
|
||||
|
||||
def check_value(self, value):
|
||||
pass
|
||||
|
||||
def check(self, key, value):
|
||||
try:
|
||||
self.check_key(key)
|
||||
except KeyError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise KeyError(e.message)
|
||||
|
||||
try:
|
||||
self.check_value(value)
|
||||
except ValueError:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise ValueError(e.message)
|
||||
|
||||
def register(self, key, value):
|
||||
self.__setitem__(key, value)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.check(key, value)
|
||||
super().__setitem__(key, value)
|
||||
|
||||
|
||||
def flatten(data):
|
||||
"""
|
||||
Turns data structured in nested iterables into data suitable for filtering
|
||||
|
|
Reference in New Issue