Skip to content

Commit

Permalink
#7 Adds support for filtering objects
Browse files Browse the repository at this point in the history
  • Loading branch information
bytebutcher committed Jul 6, 2023
1 parent eb5d06d commit 29d3e63
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 4 deletions.
3 changes: 2 additions & 1 deletion pydictdisplayfilter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from pydictdisplayfilter.display_filters import DictDisplayFilter, ListDisplayFilter
from pydictdisplayfilter.display_filters import \
DictDisplayFilter, ListDisplayFilter, ObjectDisplayFilter, SQLDisplayFilter
34 changes: 32 additions & 2 deletions pydictdisplayfilter/display_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def filter(self, display_filter: str):


class DictDisplayFilter(BaseDisplayFilter):
""" Allows to filter a dictionary using a display filter. """
""" Allows to filter a list of dictionaries using a display filter. """

def __init__(self,
data: List[dict],
Expand All @@ -138,7 +138,7 @@ def __init__(self,
self._data = data

def filter(self, display_filter: str):
""" Filters the data using the display filter. """
""" Filters the dictionaries using the display filter. """
expressions = self._display_filter_parser.parse(display_filter)
yield from self._filter_data(self._data, expressions)

Expand Down Expand Up @@ -246,3 +246,33 @@ def filter(self, display_filter: str):
expressions = self._display_filter_parser.parse(display_filter)
table_data = self._get_table_data()
yield from self._filter_data(table_data, expressions)


class ObjectDisplayFilter(BaseDisplayFilter):
""" Allows to filter a list of objects using a display filter. """

def __init__(self,
data: List[object],
field_names: List[str] = None,
functions: Dict[str, Callable] = None,
slicers: List[BasicSlicer] = None,
evaluator: Evaluator = None):
"""
Initializes the DictDisplayFilter.
:param data: A list of dictionaries to filter on.
"""
super().__init__(field_names=field_names, functions=functions, slicers=slicers, evaluator=evaluator)
self._data = data

def _filter_data(self, data: List[object], expressions: List[Union[Expression, str]]) -> List:
if expressions:
for item in data:
if self._evaluate_expressions(expressions, item.__dict__):
yield item
else:
yield from data

def filter(self, display_filter: str):
""" Filters the objects using the display filter. """
expressions = self._display_filter_parser.parse(display_filter)
yield from self._filter_data(self._data, expressions)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# This call to setup() does all the work
setup(
name="python-dict-display-filter",
version="1.1.0",
version="1.2.0",
description="A Wireshark-like display filter for dictionaries.",
long_description=README,
long_description_content_type="text/markdown",
Expand Down
93 changes: 93 additions & 0 deletions tests/test_object_display_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# vim: ts=8:sts=8:sw=8:noexpandtab
#
# This file is part of python-dict-display-filter.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import dataclasses
import unittest
from typing import List

from parameterized import parameterized

from pydictdisplayfilter.display_filters import ObjectDisplayFilter


class Person:

def __init__(self, name: str = '', age: int = 0, gender: str = '', killed: bool = False, power: List = None):
self.name = name
self.age = age
self.gender = gender
self.killed = killed
self.power = power


class TestObjectDisplayFilter(unittest.TestCase):
# List of objects representing the data to filter on.
data = [
Person(name="Morpheus", age=38, gender="male", killed=False),
Person(name="Neo", age=35, gender="male", killed=False, power=["flight", "bullet-time"]),
Person(name="Cipher", age=48, gender="male", killed=True),
Person(name="Trinity", age=32, gender="female", killed=False),
]

@parameterized.expand([
# Field existence
['name', 4],
['power', 1],
['not power', 3],
# Comparison operators
['name == Neo', 1],
['name == \x4e\x65\x6f', 1],
['killed == True', 1],
['gender == male', 3],
['age == 32', 1],
['age >= 32', 4],
['age > 32', 3],
['age <= 32', 1],
['age <= 040', 1], # octal value
['age <= 0x20', 1], # hexadecimal value
['age < 32', 0],
['age ~= 3', 3], # contains operator
['age ~ 3', 3], # matches operator
['age & 0x20', 4], # bitwise and operator
# In operator
['age in { 32, 35, 38 }', 3],
['age in { 30..40 }', 3],
['age in { 30-40 }', 3],
['age in { 30.0..40.0 }', 3],
['name in { "Neo", "Trinity" }', 2],
# logical operators
['age >= 32 and gender == male', 3],
['name == Neo or name == Trinity', 2],
['gender == female xor power', 2],
['gender == male and (age > 30 and age < 40)', 2],
['gender == male and not (age > 35)', 1],
['gender == male and !(age > 35)', 1],
# Functions
('len(name) == 3', 1),
('upper(name) == NEO', 1),
('lower(name) == neo', 1),
# Slice Operator
('gender[0] == m', 3),
('gender[-1] == e', 4),
('gender[0:2] == ma', 3),
('gender[:2] == ma', 3),
('gender[2:] == le', 3),
('gender[1-2] == ma', 3),
('gender[0,1] == ma', 3),
('gender[:2,3-4] == male', 3)
])
def test_object_display_filter_returns_correct_number_of_items(self, display_filter, no_items):
self.assertEqual(len(list(ObjectDisplayFilter(self.data).filter(display_filter))), no_items)

0 comments on commit 29d3e63

Please sign in to comment.