A generic filtering framework using abstract composition
Project description
🔍 pfylter
pfylter is a lightweight, flexible, and extensible Python framework for applying composable filters to arbitrary data. It’s built using the composite design pattern, allowing complex logical conditions to be expressed and reused cleanly.
📑 Table of Contents
🚀 Features
- ✅ Define your own filters by subclassing
AbstractFilter - ✅ Combine filters using logical AND (
AllFilters) or OR (AnyFilter) - ✅ Support for generic data types (strings, numbers, objects, etc.)
- ✅ Clean, readable syntax using list comprehensions and type hints
- ✅ Perfect for data processing, rule engines, and validation pipelines
📦 Installation
pip install pfylter
✨ Quick Start
These simple examples with number uses the LambdaFilter class to build filters based on lambda functions.
Let's start with a simple filter to get numbers greater than 5 or it's oposite condition using NotFilter.
from pfylter.core import LambdaFilter, NotFilter, AllFilters, AnyFilter
example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print('Numbers greater than 5:')
print(LambdaFilter(lambda x: x > 5).apply(example)) # [6, 7, 8, 9, 10]
print('Numbers equal or lower than 5:')
print(NotFilter(LambdaFilter(lambda x: x > 5)).apply(example)) # [1, 2, 3, 4, 5]
Now, use AllFilters and AnyFilter to create filters by aggregating other filters. When AllFilters is used, only elements that meet all filters in the list are kept.
print('Numbers greater than 5 and divisible by two:')
print(AllFilters([
LambdaFilter(lambda x: x > 5),
LambdaFilter(lambda x: x % 2 == 0)
]).apply(example)) # [6, 8, 10]
print('Numbers greater than 5 and divisible by three:')
print(AllFilters([
LambdaFilter(lambda x: x > 5),
LambdaFilter(lambda x: x % 3 == 0)
]).apply(example)) # [6, 9]
When AnyFilter, elements that meet any of the filters in the list are kept (i.e. meet any of the filters is enough to be in the output).
print('Numbers greater than 5 or divisible by two:')
print(AnyFilter([
LambdaFilter(lambda x: x > 5),
LambdaFilter(lambda x: x % 2 == 0)
]).apply(example)) # [2, 4, 6, 7, 8, 9, 10]
🧩 Predefined String Filters
The pfylter.strings module provides ready-to-use filters for common string operations:
LenFilter(length): keeps strings of a given length.StartsWithFilter(prefix): keeps strings that start with a prefix.ContainsFilter(substring): keeps strings that contain a substring.
Starting with a list of strings, here we have some uses of these basic filters.
from pfylter.strings import LenFilter, StartsWithFilter, ContainsFilter, NotFilter
from pfylter.core import AllFilters, AnyFilter
example = ['A', 'ABCD', 'B', 'BCDE', 'C', 'AAAAAAA']
print('Strings containing "BC":')
print(ContainsFilter('BC').apply(example))
print('Strings with length one:')
print(LenFilter(1).apply(example))
print('Strings with length different than one:')
print(NotFilter(LenFilter(1)).apply(example))
print('Strings with length four and starting with "A":')
print(AllFilters([LenFilter(4), StartsWithFilter('A')]).apply(example))
print('Strings with length four or starting with "A":')
print(AnyFilter([LenFilter(4), StartsWithFilter('A')]).apply(example))
More complex filters can be created creating an AnyFilter with two AllFilters objects to output all strings that either have length four and start with "A" or have length one and start with "B".
print('Strings with length four and starting with "A" or length one and starting with "B":')
print(AnyFilter([
AllFilters([LenFilter(4), StartsWithFilter('A')]),
AllFilters([LenFilter(1), StartsWithFilter('B')])
]).apply(example)) # ['ABCD', 'B']
Finally, the NotFilter can be combined with AllFilters or AnyFilter to create exclusion filters. These two examples are equivalent and allow excluding strings that contain "BC" (this excludes "ABCD" and "BCDE") or have length 1 (this excludes "A", "B, and "C").
print('Exclude any string that includes BC or has length 1 (using AllFilters):')
print(AllFilters([NotFilter(LenFilter(1)), NotFilter(ContainsFilter('BC'))]).apply(example)) # ['AAAAAAA']
print('Exclude any string that includes BC or has length 1 (using AnyFilter):')
print(NotFilter(AnyFilter([ContainsFilter('BC'), LenFilter(1)])).apply(example)) # ['AAAAAAA']
🛠 Creating Custom Filters
You can define custom filters by inheriting from AbstractFilter:
from pfylter import AbstractFilter
class GreaterThanFilter(AbstractFilter[int]):
def __init__(self, threshold: int):
self.threshold = threshold
def keep(self, instance: int) -> bool:
return instance > self.threshold
Now you can use this filter in combination with others!
📝 License
MIT License — see LICENSE file for details.
🤝 Contributing
Feel free to open issues or pull requests. All feedback is welcome!
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pfylter-0.2.1.tar.gz.
File metadata
- Download URL: pfylter-0.2.1.tar.gz
- Upload date:
- Size: 5.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7e2af09eb58439c2bb5c6a4bd97083023844c95f878804b4c839e9d5e849ea9
|
|
| MD5 |
9d550034c7b9f5887a685a73d90dedfa
|
|
| BLAKE2b-256 |
bc0b2df8143f1251eff75cb0cff551c1c05af473ef3bd667fdd637ad3c9b2a5d
|
File details
Details for the file pfylter-0.2.1-py3-none-any.whl.
File metadata
- Download URL: pfylter-0.2.1-py3-none-any.whl
- Upload date:
- Size: 5.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26be980efe13bdd8337986fe43aef491ef8c37e20e56bca6173981d5317eba8c
|
|
| MD5 |
5925263e2689cf4b6fb1c37534973502
|
|
| BLAKE2b-256 |
60ea018dbef1a7c5674575520e96c8e46a97fb9f2b509cb0a51995bc17bd3523
|