lucidiot/stdqs
lucidiot
/
stdqs
Archived
1
0
Fork 0

Use objtools.registry

This commit is contained in:
Lucidiot 2019-10-07 06:18:45 +02:00
parent e75fb71581
commit d99a9b36ef
Signed by: lucidiot
GPG Key ID: 3358C1CA6906FB8D
5 changed files with 50 additions and 113 deletions

View File

@ -0,0 +1 @@
objtools>=0.1

View File

@ -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')

View File

@ -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)

View File

@ -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), \

View File

@ -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