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

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

View File

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

View File

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