From 6e7b2d759c365d5d68acdb5176d163a5faacd3af Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 16 Apr 2022 13:59:03 -0500 Subject: [PATCH] feat: QueryList: Filter lists of dictionaries w/ nested support --- libvcs/utils/query_list.py | 38 +++++++++++++++++++++++----------- tests/utils/test_query_list.py | 2 +- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/libvcs/utils/query_list.py b/libvcs/utils/query_list.py index 6c6456df3..4d0a4c503 100644 --- a/libvcs/utils/query_list.py +++ b/libvcs/utils/query_list.py @@ -1,6 +1,7 @@ +import dataclasses import re import traceback -from typing import Any, Callable, Optional, Protocol, Sequence, TypeVar, Union +from typing import Any, Callable, Generic, Optional, Protocol, Sequence, TypeVar, Union T = TypeVar("T", Any, Any) @@ -120,9 +121,13 @@ def lookup_iregex(data, rhs): } -class QueryList(list[T]): +@dataclasses.dataclass(eq=False) +class QueryList(Generic[T]): """Filter list of object/dicts. For small, local datasets. *Experimental, unstable*. + :py:func:`dataclasses.dataclass` is only used for ``__repr__`` and pytest comparison + details. + >>> query = QueryList( ... [ ... { @@ -139,35 +144,44 @@ class QueryList(list[T]): ... }, ... ] ... ) - >>> query.filter(place="Chicago suburbs")[0]['city'] + >>> query.filter(place="Chicago suburbs").data[0]['city'] 'Elmhurst' - >>> query.filter(place__icontains="chicago")[0]['city'] + >>> query.filter(place__icontains="chicago").data[0]['city'] 'Elmhurst' - >>> query.filter(foods__breakfast="waffles")[0]['city'] + >>> query.filter(foods__breakfast="waffles").data[0]['city'] 'Elmhurst' - >>> query.filter(foods__fruit__in="cantelope")[0]['city'] + >>> query.filter(foods__fruit__in="cantelope").data[0]['city'] 'Elmhurst' - >>> query.filter(foods__fruit__in="orange")[0]['city'] + >>> query.filter(foods__fruit__in="orange").data[0]['city'] 'Tampa' """ + __slots__ = ("data", "pk_key") data: Sequence[T] + # def __init__(self, data, pk_key: Optional[str] = None): + # self.data: Sequence[T] = data + # #: Primary key for objects, optional. + # #: Use for .get(), .items() + # self.pk_key: Optional[Any] = pk_key + def items(self): data: Sequence[T] if self.pk_key is None: raise Exception("items() require a pk_key exists") - return [(getattr(item, self.pk_key), item) for item in self] + return [(getattr(item, self.pk_key), item) for item in self.data] def __eq__(self, other): data = other + if hasattr(data, "data"): + data = getattr(data, "data") - if not isinstance(self, list) or not isinstance(data, list): + if not isinstance(self.data, list) or not isinstance(data, list): return False - if len(self) == len(data): - for (a, b) in zip(self, data): + if len(self.data) == len(data): + for (a, b) in zip(self.data, data): if isinstance(a, dict): a_keys = a.keys() if a.keys == b.keys(): @@ -216,4 +230,4 @@ def val_match(obj): else: _filter = filter_lookup - return self.__class__(k for k in self if _filter(k)) + return self.__class__(data=[k for k in self.data if _filter(k)]) diff --git a/tests/utils/test_query_list.py b/tests/utils/test_query_list.py index 6e0da2b17..a2b597cb1 100644 --- a/tests/utils/test_query_list.py +++ b/tests/utils/test_query_list.py @@ -230,7 +230,7 @@ ], ) def test_filter(items: list, filter_expr: Optional[dict], expected_result: list): - qs = QueryList(items) + qs = QueryList(data=items) if filter_expr is not None: if isinstance(filter_expr, dict): assert qs.filter(**filter_expr) == expected_result pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy