diff --git a/Integrations/python/deephaven/__init__.py b/Integrations/python/deephaven/__init__.py index b6987f81b3e..15d691abe17 100644 --- a/Integrations/python/deephaven/__init__.py +++ b/Integrations/python/deephaven/__init__.py @@ -90,7 +90,8 @@ ParquetTools as pt, \ TableTools as ttools, \ TableLoggers as tloggers, \ - Types as dh + Types as dh, \ + filter as _filter from .Plot import figure_wrapper as figw @@ -119,6 +120,7 @@ def initialize(): ttools._defineSymbols() tloggers._defineSymbols() csv._defineSymbols() + _filter._defineSymbols() import deephaven.TableManipulation deephaven.TableManipulation._defineSymbols() diff --git a/Integrations/python/deephaven/filter.py b/Integrations/python/deephaven/filter.py new file mode 100644 index 00000000000..772f1fe0f45 --- /dev/null +++ b/Integrations/python/deephaven/filter.py @@ -0,0 +1,59 @@ +# +# Copyright (c) 2016-2021 Deephaven Data Labs and Patent Pending +# +""" The deephaven.filter module provides methods for filtering Deephaven tables.""" + +import jpy +import wrapt + +_JFilter = None +_JFilterOr = None + + +def _defineSymbols(): + """ + Defines appropriate java symbol, which requires that the jvm has been initialized through the :class:`jpy` module, + for use throughout the module AT RUNTIME. This is versus static definition upon first import, which would lead to an + exception if the jvm wasn't initialized BEFORE importing the module. + """ + + if not jpy.has_jvm(): + raise SystemError("No java functionality can be used until the JVM has been initialized through the jpy module") + + global _JFilter, _JFilterOr + + if _JFilter is None: + # This will raise an exception if the desired object is not the classpath + _JFilter = jpy.get_type("io.deephaven.api.filter.Filter") + _JFilterOr = jpy.get_type("io.deephaven.api.filter.FilterOr") + + +# every module method should be decorated with @_passThrough +@wrapt.decorator +def _passThrough(wrapped, instance, args, kwargs): + """ + For decoration of module methods, to define necessary symbols at runtime + + :param wrapped: the method to be decorated + :param instance: the object to which the wrapped function was bound when it was called + :param args: the argument list for `wrapped` + :param kwargs: the keyword argument dictionary for `wrapped` + :return: the decorated version of the method + """ + + _defineSymbols() + return wrapped(*args, **kwargs) + + +@_passThrough +def or_(*filters) -> object: + """ Filters on cases where any of the input filters evaluate to true. + + Args: + *filters (str): the filter expressions + + Returns: + a filter that evaluates to true when any of the input filters evaluates to true + """ + + return _JFilterOr.of(_JFilter.from_(filters)) diff --git a/Integrations/python/test/test_filter.py b/Integrations/python/test/test_filter.py new file mode 100644 index 00000000000..9e7fa56df5d --- /dev/null +++ b/Integrations/python/test/test_filter.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2016-2021 Deephaven Data Labs and Patent Pending +# +import unittest + +from deephaven.TableTools import newTable, intCol, diff +from deephaven.filter import or_ +from unittest import TestCase + + +class FilterTestCase(TestCase): + def test_or(self): + t = newTable( + intCol("A", 1, 2, 3, 4, 5), + intCol("B", 11, 12, 13, 14, 15) + ) + + t_target = newTable( + intCol("A", 1, 4, 5), + intCol("B", 11, 14, 15) + ) + + t_actual = t.where(or_("A<2", "B>=14")) + + diff_string = diff(t_actual, t_target, 1) + self.assertEqual("", diff_string) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file