List querying - libvcs._internal.query_list¶
QueryList is the backbone of the
Manager/Cmd pattern. Every ls() method in
libvcs returns a QueryList, enabling chainable filtering on the results.
How It’s Used¶
All Manager classes return QueryList from their ls() methods —
git.branches.ls() yields QueryList[GitBranchCmd], git.tags.ls() yields
QueryList[GitTagCmd], and so on:
>>> from libvcs.cmd.git import Git
>>> from libvcs._internal.query_list import QueryList
>>> git = Git(path=example_git_repo.path)
>>> isinstance(git.branches.ls(), QueryList)
True
>>> isinstance(git.tags.ls(), QueryList)
True
>>> isinstance(git.remotes.ls(), QueryList)
True
Filtering¶
QueryList extends Python’s built-in list with
Django-style
lookups.
Filter on any attribute of the contained objects — exact by default, or with
a lookup suffix:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.branches.create(branch='feature-a')
''
>>> git.branches.ls().filter(branch_name='master')
[<GitBranchCmd ... branch_name=master>]
>>> git.branches.ls().filter(branch_name__icontains='FEATURE')
[<GitBranchCmd ... branch_name=feature-a>]
Lookups traverse nested structures with __ as well:
>>> from libvcs._internal.query_list import QueryList
>>> cities = QueryList([
... {'city': 'Tampa', 'weather': {'sky': 'sunny'}},
... {'city': 'Chicago', 'weather': {'sky': 'windy'}},
... ])
>>> cities.filter(weather__sky='sunny')
[{'city': 'Tampa', 'weather': {'sky': 'sunny'}}]
Available Lookups¶
Lookup |
Description |
|---|---|
|
Exact match (default) |
|
Case-insensitive exact match |
|
Substring match |
|
Case-insensitive substring |
|
Prefix match |
|
Case-insensitive prefix |
|
Suffix match |
|
Case-insensitive suffix |
|
Value in list |
|
Value not in list |
|
Regular expression match |
|
Case-insensitive regex |
Chaining¶
Filters can be chained and combined — multiple conditions in one call AND
together, and get() retrieves exactly one match:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.branches.create(branch='feature-login')
''
>>> git.branches.create(branch='feature-signup')
''
>>> git.branches.ls().filter(
... branch_name__startswith='feature',
... branch_name__endswith='login',
... )
[<GitBranchCmd ... branch_name=feature-login>]
>>> git.branches.ls().filter(
... branch_name__contains='feature'
... ).filter(branch_name__contains='signup')
[<GitBranchCmd ... branch_name=feature-signup>]
>>> branch = git.branches.get(branch_name='master')
>>> branch.branch_name
'master'
API Reference¶
Utilities for filtering or searching list of objects / list data.
Note
This is an internal API not covered by versioning policy.
-
exception libvcs._internal.query_list.MultipleObjectsReturned¶exception libvcs._internal.query_list.MultipleObjectsReturned¶
Bases:
ExceptionThe query returned multiple objects when only one was expected.
-
exception libvcs._internal.query_list.ObjectDoesNotExist¶exception libvcs._internal.query_list.ObjectDoesNotExist¶
Bases:
ExceptionThe requested object does not exist.
-
libvcs._internal.query_list.keygetter(obj, path)¶libvcs._internal.query_list.keygetter(obj, path)¶
Fetch values in objects and keys, supported nested data.
With dictionaries:
>>> keygetter({ "food": { "breakfast": "cereal" } }, "food") {'breakfast': 'cereal'}
>>> keygetter({ "food": { "breakfast": "cereal" } }, "food__breakfast") 'cereal'
With objects:
>>> from typing import Optional >>> from dataclasses import dataclass, field
>>> @dataclass() ... class Food: ... fruit: list[str] = field(default_factory=list) ... breakfast: Optional[str] = None
>>> @dataclass() ... class Restaurant: ... place: str ... city: str ... state: str ... food: Food = field(default_factory=Food)
>>> restaurant = Restaurant( ... place="Largo", ... city="Tampa", ... state="Florida", ... food=Food( ... fruit=["banana", "orange"], breakfast="cereal" ... ) ... )
>>> restaurant Restaurant(place='Largo', city='Tampa', state='Florida', food=Food(fruit=['banana', 'orange'], breakfast='cereal'))
>>> keygetter(restaurant, "food") Food(fruit=['banana', 'orange'], breakfast='cereal')
>>> keygetter(restaurant, "food__breakfast") 'cereal'
-
libvcs._internal.query_list.parse_lookup(obj, path, lookup)¶libvcs._internal.query_list.parse_lookup(obj, path, lookup)¶
Check if field lookup key, e.g. “my__path__contains” has comparator, return val.
If comparator not used or value not found, return None.
>>> parse_lookup({ "food": "red apple" }, "food__istartswith", "__istartswith") 'red apple'
It can also look up objects:
>>> from dataclasses import dataclass
>>> @dataclass() ... class Inventory: ... food: str
>>> item = Inventory(food="red apple")
>>> item Inventory(food='red apple')
>>> parse_lookup(item, "food__istartswith", "__istartswith") 'red apple'
-
libvcs._internal.query_list.lookup_exact(data, rhs)¶libvcs._internal.query_list.lookup_exact(data, rhs)¶
-
libvcs._internal.query_list.lookup_iexact(data, rhs)¶libvcs._internal.query_list.lookup_iexact(data, rhs)¶
-
libvcs._internal.query_list.lookup_contains(data, rhs)¶libvcs._internal.query_list.lookup_contains(data, rhs)¶
-
libvcs._internal.query_list.lookup_icontains(data, rhs)¶libvcs._internal.query_list.lookup_icontains(data, rhs)¶
-
libvcs._internal.query_list.lookup_startswith(data, rhs)¶libvcs._internal.query_list.lookup_startswith(data, rhs)¶
-
libvcs._internal.query_list.lookup_istartswith(data, rhs)¶libvcs._internal.query_list.lookup_istartswith(data, rhs)¶
-
libvcs._internal.query_list.lookup_endswith(data, rhs)¶libvcs._internal.query_list.lookup_endswith(data, rhs)¶
-
libvcs._internal.query_list.lookup_iendswith(data, rhs)¶libvcs._internal.query_list.lookup_iendswith(data, rhs)¶
-
libvcs._internal.query_list.lookup_nin(data, rhs)¶libvcs._internal.query_list.lookup_nin(data, rhs)¶
-
libvcs._internal.query_list.lookup_regex(data, rhs)¶libvcs._internal.query_list.lookup_regex(data, rhs)¶
-
libvcs._internal.query_list.lookup_iregex(data, rhs)¶libvcs._internal.query_list.lookup_iregex(data, rhs)¶
-
exception libvcs._internal.query_list.PKRequiredException¶exception libvcs._internal.query_list.PKRequiredException¶
Bases:
Exception
-
exception libvcs._internal.query_list.OpNotFound¶exception libvcs._internal.query_list.OpNotFound¶
Bases:
ValueError
-
class libvcs._internal.query_list.QueryList¶class libvcs._internal.query_list.QueryList¶
Filter list of object/dictionaries. For small, local datasets.
Experimental, unstable.
With dictionaries:
>>> query = QueryList( ... [ ... { ... "place": "Largo", ... "city": "Tampa", ... "state": "Florida", ... "foods": {"fruit": ["banana", "orange"], "breakfast": "cereal"}, ... }, ... { ... "place": "Chicago suburbs", ... "city": "Elmhurst", ... "state": "Illinois", ... "foods": {"fruit": ["apple", "cantelope"], "breakfast": "waffles"}, ... }, ... ] ... )
>>> query.filter(place="Chicago suburbs")[0]['city'] 'Elmhurst' >>> query.filter(place__icontains="chicago")[0]['city'] 'Elmhurst' >>> query.filter(foods__breakfast="waffles")[0]['city'] 'Elmhurst' >>> query.filter(foods__fruit__in="cantelope")[0]['city'] 'Elmhurst' >>> query.filter(foods__fruit__in="orange")[0]['city'] 'Tampa'
>>> query.filter(foods__fruit__in="apple") [{'place': 'Chicago suburbs', 'city': 'Elmhurst', 'state': 'Illinois', 'foods': {'fruit': ['apple', 'cantelope'], 'breakfast': 'waffles'}}]
>>> query.filter(foods__fruit__in="non_existent") []
With objects:
>>> from typing import Any >>> from dataclasses import dataclass, field
>>> @dataclass() ... class Restaurant: ... place: str ... city: str ... state: str ... foods: dict[str, Any]
>>> restaurant = Restaurant( ... place="Largo", ... city="Tampa", ... state="Florida", ... foods={ ... "fruit": ["banana", "orange"], "breakfast": "cereal" ... } ... )
>>> restaurant Restaurant(place='Largo', city='Tampa', state='Florida', foods={'fruit': ['banana', 'orange'], 'breakfast': 'cereal'})
>>> query = QueryList([restaurant])
>>> query.filter(foods__fruit__in="banana") [Restaurant(place='Largo', city='Tampa', state='Florida', foods={'fruit': ['banana', 'orange'], 'breakfast': 'cereal'})]
>>> query.filter(foods__fruit__in="banana")[0].city 'Tampa'
>>> query.get(foods__fruit__in="banana").city 'Tampa'
With objects (nested):
>>> from typing import Optional >>> from dataclasses import dataclass, field
>>> @dataclass() ... class Food: ... fruit: list[str] = field(default_factory=list) ... breakfast: Optional[str] = None
>>> @dataclass() ... class Restaurant: ... place: str ... city: str ... state: str ... food: Food = field(default_factory=Food)
>>> query = QueryList([ ... Restaurant( ... place="Largo", ... city="Tampa", ... state="Florida", ... food=Food( ... fruit=["banana", "orange"], breakfast="cereal" ... ) ... ), ... Restaurant( ... place="Chicago suburbs", ... city="Elmhurst", ... state="Illinois", ... food=Food( ... fruit=["apple", "cantelope"], breakfast="waffles" ... ) ... ) ... ])
>>> query.filter(food__fruit__in="banana") [Restaurant(place='Largo', city='Tampa', state='Florida', food=Food(fruit=['banana', 'orange'], breakfast='cereal'))]
>>> query.filter(food__fruit__in="banana")[0].city 'Tampa'
>>> query.get(food__fruit__in="banana").city 'Tampa'
>>> query.filter(food__breakfast="waffles") [Restaurant(place='Chicago suburbs', city='Elmhurst', state='Illinois', food=Food(fruit=['apple', 'cantelope'], breakfast='waffles'))]
>>> query.filter(food__breakfast="waffles")[0].city 'Elmhurst'
>>> query.filter(food__breakfast="non_existent") []