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 abc import ABCMeta, abstractmethod
|
||||||
from stdqs.utils import Registry
|
from objtools.registry import ClassRegistry
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
|
|
||||||
class LookupMetaclass(ABCMeta):
|
class LookupRegistry(ClassRegistry):
|
||||||
|
|
||||||
def __new__(cls, name, *args, **kwargs):
|
def check_key(self, key):
|
||||||
newclass = ABCMeta.__new__(cls, name, *args, **kwargs)
|
assert isinstance(key, str), 'Lookup name must be a string.'
|
||||||
if name != "Lookup":
|
|
||||||
register(newclass)
|
def check_value(self, value):
|
||||||
return newclass
|
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`.
|
A filtering operation: The "lt" in `field__lt=4`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
lookup_name = None
|
|
||||||
|
|
||||||
def __init__(self, lhs, rhs):
|
def __init__(self, lhs, rhs):
|
||||||
self.lhs, self.rhs = lhs, rhs
|
self.lhs, self.rhs = lhs, rhs
|
||||||
|
|
||||||
|
@ -55,54 +63,12 @@ def func_lookup(func, name=None):
|
||||||
{
|
{
|
||||||
'__module__': func_lookup.__module__,
|
'__module__': func_lookup.__module__,
|
||||||
'__qualname__': class_name,
|
'__qualname__': class_name,
|
||||||
'lookup_name': name,
|
|
||||||
'filter_obj': lambda self, obj: func(obj, self.rhs)
|
'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)
|
LessThanLookup = func_lookup(operator.lt)
|
||||||
GreaterThanLookup = func_lookup(operator.gt)
|
GreaterThanLookup = func_lookup(operator.gt)
|
||||||
LessOrEqualLookup = func_lookup(operator.le, name='lte')
|
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 abc import ABCMeta, abstractmethod
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
|
|
||||||
|
|
||||||
class TransformMetaclass(LookupMetaclass):
|
class TransformRegistry(LookupRegistry):
|
||||||
|
|
||||||
def __new__(cls, name, *args, **kwargs):
|
def check_value(self, value):
|
||||||
newclass = ABCMeta.__new__(cls, name, *args, **kwargs)
|
assert issubclass(value, Transform), \
|
||||||
if name not in ("Transform", "GetterTransform"):
|
'Transform must inherit from the Transform base class'
|
||||||
register(newclass)
|
|
||||||
return newclass
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
A special kind of lookup that also allows lookups on itself.
|
||||||
For example, the "abs" in `field__abs__lt=2`.
|
For example, the "abs" in `field__abs__lt=2`.
|
||||||
|
@ -20,7 +27,7 @@ class Transform(Lookup, metaclass=TransformMetaclass):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, lhs, rhs=None):
|
def __init__(self, lhs, rhs=None):
|
||||||
super().__init__(lhs, rhs)
|
self.lhs, self.rhs = lhs, rhs
|
||||||
|
|
||||||
def filter_obj(self, obj):
|
def filter_obj(self, obj):
|
||||||
transformed = self.transform_obj(obj)
|
transformed = self.transform_obj(obj)
|
||||||
|
@ -36,6 +43,13 @@ class Transform(Lookup, metaclass=TransformMetaclass):
|
||||||
return NotImplemented.
|
return NotImplemented.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '{}({!r},{!r})'.format(
|
||||||
|
self.__class__.__name__,
|
||||||
|
self.lhs,
|
||||||
|
self.rhs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def func_transform(func, name=None):
|
def func_transform(func, name=None):
|
||||||
"""
|
"""
|
||||||
|
@ -54,13 +68,13 @@ def func_transform(func, name=None):
|
||||||
{
|
{
|
||||||
'__module__': func_transform.__module__,
|
'__module__': func_transform.__module__,
|
||||||
'__qualname__': class_name,
|
'__qualname__': class_name,
|
||||||
'lookup_name': name,
|
|
||||||
'transform_obj': lambda self, obj: func(obj)
|
'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.
|
A special Transform that fetches values from attributes, keys or methods.
|
||||||
Should not be registered as a normal Transform.
|
Should not be registered as a normal Transform.
|
||||||
|
@ -84,18 +98,6 @@ class GetterTransform(Transform):
|
||||||
return None
|
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)
|
AbsoluteTransform = func_transform(abs)
|
||||||
AsciiTransform = func_transform(ascii)
|
AsciiTransform = func_transform(ascii)
|
||||||
BinaryTransform = func_transform(bin)
|
BinaryTransform = func_transform(bin)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from collections.abc import Sequence, Collection, Iterator
|
from collections.abc import Sequence, Collection, Iterator
|
||||||
|
from objtools.registry import ClassRegistry
|
||||||
from stdqs.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
from stdqs.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||||
from stdqs.utils import Registry, flatten
|
from stdqs.utils import flatten
|
||||||
from stdqs.parsing import QueryParser
|
from stdqs.parsing import QueryParser
|
||||||
|
|
||||||
REPR_OUTPUT_SIZE = 20
|
REPR_OUTPUT_SIZE = 20
|
||||||
|
@ -165,7 +166,7 @@ class DictQuerySet(QuerySet):
|
||||||
return flatten(self.collection)
|
return flatten(self.collection)
|
||||||
|
|
||||||
|
|
||||||
class QuerySetRegistry(Registry):
|
class QuerySetRegistry(ClassRegistry):
|
||||||
|
|
||||||
def check_key(self, key):
|
def check_key(self, key):
|
||||||
assert isinstance(key, type), \
|
assert isinstance(key, type), \
|
||||||
|
|
|
@ -1,39 +1,6 @@
|
||||||
from collections.abc import Mapping, Iterable
|
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):
|
def flatten(data):
|
||||||
"""
|
"""
|
||||||
Turns data structured in nested iterables into data suitable for filtering
|
Turns data structured in nested iterables into data suitable for filtering
|
||||||
|
|
Reference in New Issue