Sequence filtering¶
The momotor.bundles.utils.filters
package provides two filterable sequences, the
FilterableList
and
FilterableTuple
.
Both add the same functions to the standard python list
and tuple
types.
The interface is loosely based on Django QuerySets field lookups.
The examples in this documentation use the following filterable lists:
>>> data_str = FilterableList([ItemA(a='abc'), ItemA(a='ABC'), ItemA(a='def'), ItemA(a='abc/def')])
>>> data_int = FilterableList([ItemA(a=1), ItemA(a=2), ItemA(a=3)])
>>> data_set = FilterableList([ItemA(a={1, 2}), ItemA(a={2, 3}), ItemA(a={3, 4})])
>>> data_ab = FilterableList([ItemAB(a=1, b=1), ItemAB(a=1, b=2), ItemAB(a=2, b=2), ItemAB(a=3, b=3)])
Where ItemA and ItemAB are datatypes having both an a attribute, and ItemAB also has a b attribute.
Filtering and excluding¶
The most basic lookup looks like this:
>>> data_ab.filter(a=1) # filter all items with a==1
[ItemAB(a=1, b=1), ItemAB(a=1, b=2)]
This filters data_ab for any objects containing an a attribute with value equal to 1
.
The result of filter()
is itself another filterable type,
so filters can be chained:
>>> data_ab.filter(a=1).filter(b=2) # filter all items with a==1, the filter the result for a==2
[ItemAB(a=1, b=2)]
FilterableList.exclude(...)
excludes items:
>>> data_ab.exclude(a=1) # exclude all items where a==1
[ItemAB(a=2, b=2), ItemAB(a=3, b=3)]
Providing multiple lookups in filter()
or exclude()
will filter or exclude objects matching all the lookups:
>>> data_ab.filter(a=1, b=2) # filter all items with a==1 and b==2
[ItemAB(a=1, b=2)]
>>> data_ab.exclude(a=1, b=2) # exclude all items with a==1 and b==2
[ItemAB(a=1, b=1), ItemAB(a=2, b=2), ItemAB(a=3, b=3)]
Not() object¶
Lookups can be negated using a Not
object:
>>> data_ab.filter(Not(a=1)) # filter all items where `a` is not 1
[ItemAB(a=2, b=2), ItemAB(a=3, b=3)]
FilterableList.exclude(...)
is a shortcut for FilterableList.filter(Not(...))
All() object¶
The All
object can be used to make lookups with multiple arguments
more explicit:
>>> data_ab.filter(All(a=1, b=2)) # filter all items with a==1 and b==2
[ItemAB(a=1, b=2)]
Any() object¶
Lookups can also be combined using the Any
object to find an object
which matches any of the requested lookups:
>>> data_ab.filter(Any(a=1, b=2)) # filter all items with a==1 OR b==2
[ItemAB(a=1, b=1), ItemAB(a=1, b=2), ItemAB(a=2, b=2)]
Combining filter objects¶
Lookup objects can be combined, for example:
>>> data_ab.filter(Any(All(a=1, b=2), b=3)) # Look for items with (a==1 AND b==2) OR b==3
[ItemAB(a=1, b=2), ItemAB(a=3, b=3)]
F() object¶
Using keyword arguments it is not possible to filter for multiple values of the same property, since
filter(a=1, a=3)
has a repeated keyword a
:
>>> data_ab.filter(a=1, a=3) # this is a SyntaxError
Traceback (most recent call last):
...
SyntaxError: keyword argument repeated
To look for data_ab
values for the same attribute, use the F
object
to wrap individual queries:
>>> data_ab.filter(F(a=1), F(a=3)) # filter all items with a==1 AND a==3 (which is nonsensical indeed, but this is just an example)
[]
This can also be combined with the other filter objects like Any
>>> data_ab.filter(Any(F(a=1), F(a=3))) # filter items with a==1 OR a==3
[ItemAB(a=1, b=1), ItemAB(a=1, b=2), ItemAB(a=3, b=3)]
F
objects can also be used to easily pass on filter queries as
arguments to functions:
>>> def filter_data(f: F):
... return data_ab.filter(f)
>>> for f in [F(a=1), F(a=2)]:
... print(filter_data(f))
[ItemAB(a=1, b=1), ItemAB(a=1, b=2)]
[ItemAB(a=2, b=2)]
Any
, All
and
Not
are subclasses of F
.
When mixing F
arguments and keyword arguments, Python syntax requires
the positional arguments to be provided before the keyword arguments. The following is invalid:
>>> data_ab.filter(a=1, Any(a=2, b=3)) # positional argument follows keyword argument
Traceback (most recent call last):
...
SyntaxError: positional argument follows keyword argument
Using an F
object solves this issue:
>>> data_ab.filter(F(a=1), Any(a=2, b=3))
[]
Negation¶
The Any
and All
classes
support negation using the ~
operator, for example ~All(...)
is the same as Not(All(...))
Lookup types¶
Additionally to filtering or excluding exact values, there are several other lookup types.
A lookup type is added to the lookup by separating the attribute name and the lookup with a double underscore
__
, eg. a__ne
applies the not-equal lookup to the a attribute.
The following filter lookup operations are available:
Lookup type |
Case sensitive lookup |
Case insensitive lookup |
---|---|---|
Is-equal |
||
Not-equal |
||
Contains |
||
In |
||
Starts-with |
||
Ends-with |
||
Glob |
||
Recursive glob |
||
Regular expression |
Note
Case insensitive lookups only accept strings or a sequence of strings. Any other value
will raise a TypeError
:
>>> data_int.filter(a__ine=1)
Traceback (most recent call last):
...
TypeError: Expected a string or sequence of strings, got <class 'int'>
Is-equal lookup¶
Filters exact values.
Operator: No operator, is
or eq
Examples:
>>> data_str.filter(a='abc')
[ItemA(a='abc')]
>>> data_str.filter(a__is='ABC')
[ItemA(a='ABC')]
>>> data_str.filter(a__eq='def')
[ItemA(a='def')]
>>> data_int.filter(a=1)
[ItemA(a=1)]
>>> data_int.filter(a='abc')
[]
Is-equal lookup (case insensitive)¶
Filters string attributes case insensitive.
Lookup operator: ieq
/ iis
Examples:
>>> data_str.filter(a__ieq='abc')
[ItemA(a='abc'), ItemA(a='ABC')]
>>> data_int.filter(a__iis='abc')
[]
Not-equal lookup¶
Lookup operator: ne
Usage example:
>>> data_str.filter(a__ne='abc')
[ItemA(a='ABC'), ItemA(a='def'), ItemA(a='abc/def')]
>>> data_int.filter(a__ne=1)
[ItemA(a=2), ItemA(a=3)]
Not-equal lookup (case insensitive)¶
Lookup operator: ine
Usage example:
>>> data_str.filter(a__ine='abc')
[ItemA(a='def'), ItemA(a='abc/def')]
Contains lookup¶
Lookup operator: contains
Works on Python types implementing object.__contains__()
like str
, sequences and sets.
Usage example:
>>> data_str.filter(a__contains='a')
[ItemA(a='abc'), ItemA(a='abc/def')]
>>> data_set.filter(a__contains=2)
[ItemA(a={1, 2}), ItemA(a={2, 3})]
Contains lookup (case insensitive)¶
Lookup operator: icontains
Works on Python str
and sets and sequences containing strings.
Usage example:
>>> data_str.filter(a__icontains='a')
[ItemA(a='abc'), ItemA(a='ABC'), ItemA(a='abc/def')]
In lookup¶
Lookup operator: in
The reverse of contains. The lookup value must implement object.__contains__()
Usage example:
>>> data_str.filter(a__in={'abc', 'def', 'ghi'})
[ItemA(a='abc'), ItemA(a='def')]
>>> data_int.filter(a__in={0, 1, 2})
[ItemA(a=1), ItemA(a=2)]
In lookup (case insensitive)¶
Lookup operator: iin
Usage example:
>>> data_str.filter(a__iin={'abc', 'def', 'ghi'})
[ItemA(a='abc'), ItemA(a='ABC'), ItemA(a='def')]
Starts-with lookup¶
Lookup operator: startswith
Usage example:
>>> data_str.filter(a__startswith='a')
[ItemA(a='abc'), ItemA(a='abc/def')]
Starts-with lookup (case insensitive)¶
Lookup operator: istartswith
Usage example:
>>> data_str.filter(a__istartswith='a')
[ItemA(a='abc'), ItemA(a='ABC'), ItemA(a='abc/def')]
Ends-with lookup¶
Lookup operator: endswith
Usage example:
>>> data_str.filter(a__endswith='c')
[ItemA(a='abc')]
Ends-with lookup (case insensitive)¶
Lookup operator: iendswith
Usage example:
>>> data_str.filter(a__iendswith='c')
[ItemA(a='abc'), ItemA(a='ABC')]
Glob lookup¶
Lookup operator: glob
Matches a string against a pattern. The pattern is a string with special characters:
*
matches any number of characters?
matches any single character[seq]
matches any character in seq[!seq]
matches any character not in seq
Unlike the rglob filter, path separators characters (/
and \
) are not considered
special and are matched by the *
pattern.
Usage example:
>>> data_str.filter(a__glob='a*')
[ItemA(a='abc'), ItemA(a='abc/def')]
Glob lookup (case insensitive)¶
Lookup operator: iglob
Matches a string against a pattern, case insensitive. The pattern is a string with special characters:
*
matches any number of characters?
matches any single character[seq]
matches any character in seq[!seq]
matches any character not in seq
Unlike the irglob filter, path separators characters (/
and \
) are not considered
special and are matched by the *
pattern.
Usage example:
>>> data_str.filter(a__iglob='a*')
[ItemA(a='abc'), ItemA(a='ABC'), ItemA(a='abc/def')]
Recursive glob lookup¶
Lookup operator: rglob
Matches a string against a recursive glob pattern. The pattern is a string with special characters:
*
matches any number of characters, excluding path separators**
matches any number of characters, including path separators (except a trailing path separator, see below)?
matches any single character[seq]
matches any character in seq[!seq]
matches any character not in seq
To match a string ending in a path separator (/
or \
, indicating a directory path), the pattern
must explicitly end in a path separator as well, i.e. **
alone will not match strings ending with a path
separator, and **/
or **\
will only match strings ending with a path separator.
Usage example:
>>> data_str.filter(a__rglob='a*')
[ItemA(a='abc')]
>>> data_str.filter(a__rglob='a**')
[ItemA(a='abc'), ItemA(a='abc/def')]
Recursive glob lookup (case insensitive)¶
Lookup operator: irglob
Matches a string against a recursive glob pattern, case insensitive. The pattern is a string with special characters:
*
matches any number of characters, excluding path separators**
matches any number of characters, including path separators (except a trailing path separator, see below)?
matches any single character[seq]
matches any character in seq[!seq]
matches any character not in seq
To match a string ending in a path separator (/
, indicating a directory path), the pattern
must explicitly end in a path separator as well, i.e. **
alone will not match strings ending with a path
separator, and **/
or **\
will only match strings ending with a path separator.
Usage example:
>>> data_str.filter(a__irglob='a*')
[ItemA(a='abc'), ItemA(a='ABC')]
>>> data_str.filter(a__irglob='a**')
[ItemA(a='abc'), ItemA(a='ABC'), ItemA(a='abc/def')]
Regular expression lookup¶
Lookup operator: re
Usage example:
>>> data_str.filter(a__re=r'^.b.$')
[ItemA(a='abc')]
Regular expression lookup (case insensitive)¶
Lookup operator: ire
Usage example:
>>> data_str.filter(a__ire=r'^.b.$')
[ItemA(a='abc'), ItemA(a='ABC')]
Additional methods¶
FilterableList
and
FilterableTuple
have several more functions:
API¶
- class momotor.bundles.utils.filters.All(*filters, **filter_args)¶
“All” filter operator
All the lookups in the argument list will need to match for an item to match this filter, see the All object documentation for an example.
(alias of
F
)- COMBINER¶
alias of
_All
- NONE¶
alias of
_Always
- class momotor.bundles.utils.filters.Any(*filters, **filter_args)¶
“Any” filter operator
Any of the lookups in the argument list can match for an item to match this filter, see the Any object documentation for an example.
- COMBINER¶
alias of
_Any
- NONE¶
alias of
_Never
- class momotor.bundles.utils.filters.F(*filters, **filter_args)¶
Filter operator
Base for the other filter operators. Can be used to prevent multiple keywords SyntaxErrors in Python, see the F object documentation for an example.
- COMBINER¶
alias of
_All
- NONE¶
alias of
_Always
- class momotor.bundles.utils.filters.FilterableList(iterable=(), /)¶
A
list
with additional functions to filter the objects in the listSee Sequence filtering section in the documentation
- append(object, /)¶
Append object to the end of the list.
- clear()¶
Remove all items from list.
- copy()¶
Return a shallow copy of the list.
- count(value, /)¶
Return number of occurrences of value.
- exclude(*args, **kwargs)¶
Returns a filtered version of self that only includes elements that do not match the provided query
- extend(iterable, /)¶
Extend list by appending elements from the iterable.
- filter(*args, **kwargs)¶
Returns a filtered version of self that only includes elements that match the provided query
- filter_with(func)¶
Filter the items using a callable function
- index(value, start=0, stop=9223372036854775807, /)¶
Return first index of value.
Raises ValueError if the value is not present.
- insert(index, object, /)¶
Insert object before index.
- pop(index=-1, /)¶
Remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
- remove(value, /)¶
Remove first occurrence of value.
Raises ValueError if the value is not present.
- reverse()¶
Reverse IN PLACE.
- sort(*, key=None, reverse=False)¶
Sort the list in ascending order and return None.
The sort is in-place (i.e. the list itself is modified) and stable (i.e. the order of two equal elements is maintained).
If a key function is given, apply it once to each list item and sort them, ascending or descending, according to their function values.
The reverse flag can be set to sort in descending order.
- class momotor.bundles.utils.filters.FilterableTuple(iterable=(), /)¶
A
tuple
with additional functions to filter the objects in the tupleSee Sequence filtering section in the documentation
- count(value, /)¶
Return number of occurrences of value.
- exclude(*args, **kwargs)¶
Returns a filtered version of self that only includes elements that do not match the provided query
- filter(*args, **kwargs)¶
Returns a filtered version of self that only includes elements that match the provided query
- filter_with(func)¶
Filter the items using a callable function
- index(value, start=0, stop=9223372036854775807, /)¶
Return first index of value.
Raises ValueError if the value is not present.
- class momotor.bundles.utils.filters.Not(*filters, **filter_args)¶
“Not” filter operator
Any item not matching all the lookup arguments will match this filter, see the Not object documentation for an example.
- COMBINER¶
alias of
_All
- NONE¶
alias of
_Always