diff --git a/engine/context/src/main/java/io/deephaven/engine/context/ExecutionContext.java b/engine/context/src/main/java/io/deephaven/engine/context/ExecutionContext.java index 3b7a8e2b2f6..1fa751e423f 100644 --- a/engine/context/src/main/java/io/deephaven/engine/context/ExecutionContext.java +++ b/engine/context/src/main/java/io/deephaven/engine/context/ExecutionContext.java @@ -8,6 +8,7 @@ import io.deephaven.util.annotations.VisibleForTesting; import java.util.Arrays; +import java.util.Objects; import java.util.function.Supplier; public class ExecutionContext { @@ -18,17 +19,8 @@ public static Builder newBuilder() { return new Builder(); } - public static ExecutionContext makeSystemicExecutionContext() { - final ExecutionContext context = getContext(); - if (context.isSystemic) { - return context; - } - return ExecutionContext.newBuilder() - .captureQueryScope() - .captureQueryLibrary() - .captureQueryCompiler() - .markSystemic() - .build(); + public static ExecutionContext makeExecutionContext(boolean isSystemic) { + return getContext().withSystemic(isSystemic); } @VisibleForTesting @@ -104,9 +96,23 @@ private ExecutionContext( final QueryScope queryScope, final QueryCompiler queryCompiler) { this.isSystemic = isSystemic; - this.queryLibrary = queryLibrary; - this.queryScope = queryScope; - this.queryCompiler = queryCompiler; + this.queryLibrary = Objects.requireNonNull(queryLibrary); + this.queryScope = Objects.requireNonNull(queryScope); + this.queryCompiler = Objects.requireNonNull(queryCompiler); + } + + /** + * Returns, or creates, an execution context with the given value for {@code isSystemic} and existing values for the + * other members. + * + * @param isSystemic if the context should be systemic + * @return the execution context + */ + public ExecutionContext withSystemic(boolean isSystemic) { + if (isSystemic == this.isSystemic) { + return this; + } + return new ExecutionContext(isSystemic, queryLibrary, queryScope, queryCompiler); } /** diff --git a/engine/table/src/main/java/io/deephaven/engine/util/AbstractScriptSession.java b/engine/table/src/main/java/io/deephaven/engine/util/AbstractScriptSession.java index 8caa1b24282..9da8ab335ce 100644 --- a/engine/table/src/main/java/io/deephaven/engine/util/AbstractScriptSession.java +++ b/engine/table/src/main/java/io/deephaven/engine/util/AbstractScriptSession.java @@ -287,10 +287,10 @@ public VariableProvider getVariableProvider() { // ScriptSession-based QueryScope implementation, with no remote scope or object reflection support // ----------------------------------------------------------------------------------------------------------------- - private abstract static class ScriptSessionQueryScope extends QueryScope { + public abstract static class ScriptSessionQueryScope extends QueryScope { final ScriptSession scriptSession; - private ScriptSessionQueryScope(ScriptSession scriptSession) { + public ScriptSessionQueryScope(ScriptSession scriptSession) { this.scriptSession = scriptSession; } diff --git a/engine/table/src/test/java/io/deephaven/engine/table/impl/FuzzerTest.java b/engine/table/src/test/java/io/deephaven/engine/table/impl/FuzzerTest.java index 7a801288e20..25b2c5c4394 100644 --- a/engine/table/src/test/java/io/deephaven/engine/table/impl/FuzzerTest.java +++ b/engine/table/src/test/java/io/deephaven/engine/table/impl/FuzzerTest.java @@ -11,7 +11,6 @@ import io.deephaven.plugin.type.ObjectTypeLookup.NoOp; import io.deephaven.time.DateTimeUtils; import io.deephaven.engine.updategraph.UpdateGraphProcessor; -import io.deephaven.engine.context.QueryScope; import io.deephaven.time.DateTime; import io.deephaven.engine.util.TableTools; import io.deephaven.engine.util.GroovyDeephavenSession; @@ -336,7 +335,7 @@ private void validateBindingTables(GroovyDeephavenSession session, Map hardReferences) { - final ExecutionContext executionContext = ExecutionContext.makeSystemicExecutionContext(); + final ExecutionContext executionContext = ExecutionContext.makeExecutionContext(true); // noinspection unchecked session.getBinding().getVariables().forEach((k, v) -> { if (v instanceof PartitionedTable) { diff --git a/engine/table/src/test/java/io/deephaven/engine/table/impl/PartitionedTableTest.java b/engine/table/src/test/java/io/deephaven/engine/table/impl/PartitionedTableTest.java index d435f55615c..9621858eaf1 100644 --- a/engine/table/src/test/java/io/deephaven/engine/table/impl/PartitionedTableTest.java +++ b/engine/table/src/test/java/io/deephaven/engine/table/impl/PartitionedTableTest.java @@ -244,7 +244,7 @@ public void testTransformPartitionedTableThenMerge() { final PartitionedTable partitionedTable = sourceTable.partitionBy("Key"); - final ExecutionContext executionContext = ExecutionContext.makeSystemicExecutionContext(); + final ExecutionContext executionContext = ExecutionContext.makeExecutionContext(true); final EvalNuggetInterface[] en = new EvalNuggetInterface[] { new EvalNugget() { @Override @@ -753,11 +753,7 @@ public void testMergeConstituentChanges() { ExecutionContext.getContext().getQueryLibrary().importStatic(TableTools.class); final Table underlying; - try (final SafeCloseable ignored = ExecutionContext.newBuilder() - .captureQueryLibrary() - .captureQueryCompiler() - .captureQueryScope() - .build().open()) { + try (final SafeCloseable ignored = ExecutionContext.makeExecutionContext(false).open()) { underlying = base.update( "Constituent=emptyTable(1000 * step.longValue()).update(\"JJ=ii * \" + II + \" * step.longValue()\")"); } diff --git a/engine/table/src/test/java/io/deephaven/engine/table/impl/TestConcurrentInstantiation.java b/engine/table/src/test/java/io/deephaven/engine/table/impl/TestConcurrentInstantiation.java index b0f5c118125..9262b5052c3 100644 --- a/engine/table/src/test/java/io/deephaven/engine/table/impl/TestConcurrentInstantiation.java +++ b/engine/table/src/test/java/io/deephaven/engine/table/impl/TestConcurrentInstantiation.java @@ -58,7 +58,7 @@ public class TestConcurrentInstantiation extends QueryTableTestBase { @Override protected void setUp() throws Exception { super.setUp(); - final ExecutionContext executionContext = ExecutionContext.makeSystemicExecutionContext(); + final ExecutionContext executionContext = ExecutionContext.makeExecutionContext(true); final ThreadFactory threadFactory = runnable -> { Thread thread = new Thread(() -> { try (final SafeCloseable ignored = executionContext.open()) { diff --git a/py/server/deephaven/_jpy.py b/py/server/deephaven/_jpy.py new file mode 100644 index 00000000000..2615872572b --- /dev/null +++ b/py/server/deephaven/_jpy.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending +# + +""" This module is an internal module to simplify usage patterns around jpy. +""" +from __future__ import annotations + +import jpy + +from deephaven import DHError + + +def strict_cast(j_obj: jpy.JType, to_type: type) -> jpy.JType: + """A convenience function around jpy.cast. Checks that j_obj is not None and that the result is not None.""" + if not j_obj: + raise DHError(message=f"Unable to cast None into '{to_type}'") + j_obj_casted = jpy.cast(j_obj, to_type) + if not j_obj_casted: + raise DHError(message=f"Unable to cast '{j_obj.getClass()}' into '{to_type}'") + return j_obj_casted diff --git a/py/server/deephaven/dherror.py b/py/server/deephaven/dherror.py index fe8a5293be7..ab7816bcd72 100644 --- a/py/server/deephaven/dherror.py +++ b/py/server/deephaven/dherror.py @@ -23,6 +23,11 @@ class DHError(Exception): """ def __init__(self, cause=None, message=""): + if isinstance(cause, str) and message == "": + # Saves a lot of debugging headache when library code incorrectly creates something like: + # raise DHError("My error message here") + message = cause + cause = None self._message = message self._traceback = traceback.format_exc() self._cause = cause diff --git a/py/server/deephaven/table.py b/py/server/deephaven/table.py index d9e053deec1..014a9ba01e8 100644 --- a/py/server/deephaven/table.py +++ b/py/server/deephaven/table.py @@ -15,6 +15,7 @@ import jpy from deephaven import DHError, dtypes +from deephaven._jpy import strict_cast from deephaven._wrapper import JObjectWrapper from deephaven.agg import Aggregation from deephaven.column import Column, ColumnType @@ -47,12 +48,15 @@ # Dynamic Query Scope _JExecutionContext = jpy.get_type("io.deephaven.engine.context.ExecutionContext") -_JQueryScope = jpy.get_type("io.deephaven.engine.context.QueryScope") -_JUnsynchronizedScriptSessionQueryScope = jpy.get_type( - "io.deephaven.engine.util.AbstractScriptSession$UnsynchronizedScriptSessionQueryScope") +_JScriptSessionQueryScope = jpy.get_type("io.deephaven.engine.util.AbstractScriptSession$ScriptSessionQueryScope") _JPythonScriptSession = jpy.get_type("io.deephaven.integrations.python.PythonDeephavenSession") -_j_script_session = jpy.cast(_JExecutionContext.getContext().getQueryScope(), _JUnsynchronizedScriptSessionQueryScope).scriptSession() -_j_py_script_session = jpy.cast(_j_script_session, _JPythonScriptSession) + + +def _j_py_script_session() -> _JPythonScriptSession: + j_execution_context = _JExecutionContext.getContext() + j_query_scope = j_execution_context.getQueryScope() + j_script_session_query_scope = strict_cast(j_query_scope, _JScriptSessionQueryScope) + return strict_cast(j_script_session_query_scope.scriptSession(), _JPythonScriptSession) @contextlib.contextmanager @@ -72,11 +76,12 @@ def _query_scope_ctx(): if len(outer_frames) > i + 2 or function != "": scope_dict = caller_frame.f_globals.copy() scope_dict.update(caller_frame.f_locals) + j_py_script_session = _j_py_script_session() try: - _j_py_script_session.pushScope(scope_dict) + j_py_script_session.pushScope(scope_dict) yield finally: - _j_py_script_session.popScope() + j_py_script_session.popScope() else: # in the __main__ module, use the default main global scope yield diff --git a/py/server/test_helper/__init__.py b/py/server/test_helper/__init__.py index 28bebf10d94..675b012a89b 100644 --- a/py/server/test_helper/__init__.py +++ b/py/server/test_helper/__init__.py @@ -12,6 +12,10 @@ from deephaven_internal import jvm + +py_dh_session = None + + def start_jvm(jvm_props: Dict[str, str] = None): jvm.preload_jvm_dll() import jpy @@ -93,6 +97,7 @@ def start_jvm(jvm_props: Dict[str, str] = None): # Set up a Deephaven Python session py_scope_jpy = jpy.get_type("io.deephaven.engine.util.PythonScopeJpyImpl").ofMainGlobals() + global py_dh_session py_dh_session = jpy.get_type("io.deephaven.integrations.python.PythonDeephavenSession")(py_scope_jpy) py_dh_session.getExecutionContext().open() diff --git a/py/server/tests/test_calendar.py b/py/server/tests/test_calendar.py index 17c632b267d..aa318f26b9d 100644 --- a/py/server/tests/test_calendar.py +++ b/py/server/tests/test_calendar.py @@ -12,6 +12,7 @@ class CalendarTestCase(BaseTestCase): def setUp(self) -> None: + super().setUp() self.test_calendar = BusinessCalendar("USNYSE") self.b_day1 = "2022-01-03" self.b_day = "2022-03-08" @@ -22,6 +23,9 @@ def setUp(self) -> None: self.last_b_day_month = "2022-03-31" self.last_b_day_week = "2022-03-11" + def tearDown(self) -> None: + super().tearDown() + def test_calendar(self): with self.subTest(msg="calendarNames() test"): cal_names = calendar_names() diff --git a/py/server/tests/test_filters.py b/py/server/tests/test_filters.py index 0fb7fdd1247..6f37c822b29 100644 --- a/py/server/tests/test_filters.py +++ b/py/server/tests/test_filters.py @@ -11,10 +11,12 @@ class FilterTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_regex_filter(self): new_test_table = self.test_table.update("X = String.valueOf(d)") diff --git a/py/server/tests/test_numpy.py b/py/server/tests/test_numpy.py index d1e56b512df..965c04245b1 100644 --- a/py/server/tests/test_numpy.py +++ b/py/server/tests/test_numpy.py @@ -24,6 +24,7 @@ class CustomClass: class NumpyTestCase(BaseTestCase): def setUp(self): + super().setUp() j_array_list1 = j_array_list([1, -1]) j_array_list2 = j_array_list([2, -2]) input_cols = [ @@ -65,6 +66,7 @@ def setUp(self): def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_to_numpy(self): for col in self.test_table.columns: diff --git a/py/server/tests/test_pandas.py b/py/server/tests/test_pandas.py index 009623fad1a..aaf880f1a0f 100644 --- a/py/server/tests/test_pandas.py +++ b/py/server/tests/test_pandas.py @@ -26,6 +26,7 @@ class CustomClass: class PandasTestCase(BaseTestCase): def setUp(self): + super().setUp() j_array_list1 = j_array_list([1, -1]) j_array_list2 = j_array_list([2, -2]) input_cols = [ @@ -49,6 +50,7 @@ def setUp(self): def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_to_pandas(self): df = to_pandas(self.test_table) diff --git a/py/server/tests/test_partitioned_table.py b/py/server/tests/test_partitioned_table.py index dd2b7c89bb5..9200648ea1c 100644 --- a/py/server/tests/test_partitioned_table.py +++ b/py/server/tests/test_partitioned_table.py @@ -2,7 +2,6 @@ # Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending # -import jpy import unittest from deephaven.agg import partition @@ -11,7 +10,7 @@ from deephaven.filters import Filter from deephaven import read_csv, DHError, new_table, ugp, time_table -from tests.testbase import BaseTestCase +from tests.testbase import BaseTestCase, make_user_exec_ctx def transform_func(t: Table) -> Table: @@ -35,12 +34,14 @@ def apply(self, t: Table, ot: Table) -> Table: class PartitionedTableTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv").tail(num_rows=100) self.partitioned_table = self.test_table.partition_by(by=["c", "e"]) def tearDown(self): self.partitioned_table = None self.test_table = None + super().tearDown() def test_table(self): self.assertIsNotNone(self.partitioned_table.table) @@ -117,37 +118,25 @@ def test_constituents(self): self.assertGreater(len(constituent_tables), 0) def test_transform(self): - _JExecutionContext = jpy.get_type("io.deephaven.engine.context.ExecutionContext") - context = _JExecutionContext.newBuilder() \ - .captureQueryCompiler() \ - .captureQueryLibrary() \ - .emptyQueryScope() \ - .build().open() - pt = self.partitioned_table.transform(transform_func) - self.assertIn("f", [col.name for col in pt.constituent_table_columns]) - - pt = self.partitioned_table.transform(Transformer) - self.assertIn("f", [col.name for col in pt.constituent_table_columns]) + with make_user_exec_ctx(): + pt = self.partitioned_table.transform(transform_func) + self.assertIn("f", [col.name for col in pt.constituent_table_columns]) - with self.assertRaises(DHError) as cm: - pt = self.partitioned_table.transform(lambda t, t1: t.join(t1)) - self.assertRegex(str(cm.exception), r"missing .* argument") - context.close() + pt = self.partitioned_table.transform(Transformer) + self.assertIn("f", [col.name for col in pt.constituent_table_columns]) + + with self.assertRaises(DHError) as cm: + pt = self.partitioned_table.transform(lambda t, t1: t.join(t1)) + self.assertRegex(str(cm.exception), r"missing .* argument") def test_partitioned_transform(self): - _JExecutionContext = jpy.get_type("io.deephaven.engine.context.ExecutionContext") - context = _JExecutionContext.newBuilder() \ - .captureQueryCompiler() \ - .captureQueryLibrary() \ - .emptyQueryScope() \ - .build().open() - other_pt = self.partitioned_table.transform(transform_func) - pt = self.partitioned_table.partitioned_transform(other_pt, partitioned_transform_func) - self.assertIn("f", [col.name for col in pt.constituent_table_columns]) - - pt = self.partitioned_table.partitioned_transform(other_pt, PartitionedTransformer()) - self.assertIn("f", [col.name for col in pt.constituent_table_columns]) - context.close() + with make_user_exec_ctx(): + other_pt = self.partitioned_table.transform(transform_func) + pt = self.partitioned_table.partitioned_transform(other_pt, partitioned_transform_func) + self.assertIn("f", [col.name for col in pt.constituent_table_columns]) + + pt = self.partitioned_table.partitioned_transform(other_pt, PartitionedTransformer()) + self.assertIn("f", [col.name for col in pt.constituent_table_columns]) def test_partition_agg(self): with ugp.shared_lock(): diff --git a/py/server/tests/test_plot/test_color.py b/py/server/tests/test_plot/test_color.py index 80231bc61de..23f0cd40688 100644 --- a/py/server/tests/test_plot/test_color.py +++ b/py/server/tests/test_plot/test_color.py @@ -13,10 +13,12 @@ class ColorTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_color(self): figure = Figure() diff --git a/py/server/tests/test_plot/test_figure.py b/py/server/tests/test_plot/test_figure.py index 75e46803089..32aef642401 100644 --- a/py/server/tests/test_plot/test_figure.py +++ b/py/server/tests/test_plot/test_figure.py @@ -11,10 +11,12 @@ class FigureTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_figure(self): with self.subTest("Not supported yet."): diff --git a/py/server/tests/test_plot/test_font.py b/py/server/tests/test_plot/test_font.py index 4cbe77be099..41025c34283 100644 --- a/py/server/tests/test_plot/test_font.py +++ b/py/server/tests/test_plot/test_font.py @@ -13,10 +13,12 @@ class FontTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_font_family_names(self): self.assertIn("Serif", font_family_names()) diff --git a/py/server/tests/test_plot/test_linestyle.py b/py/server/tests/test_plot/test_linestyle.py index dd5f2e7ca43..2dab3d32f69 100644 --- a/py/server/tests/test_plot/test_linestyle.py +++ b/py/server/tests/test_plot/test_linestyle.py @@ -14,10 +14,12 @@ class LineStyleTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_default_line_style(self): line_style = LineStyle() diff --git a/py/server/tests/test_plot/test_plot.py b/py/server/tests/test_plot/test_plot.py index ad696330337..359ed9a6617 100644 --- a/py/server/tests/test_plot/test_plot.py +++ b/py/server/tests/test_plot/test_plot.py @@ -16,10 +16,12 @@ class PlotTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_plot_style(self): figure = Figure() diff --git a/py/server/tests/test_plot/test_selectable_dataset.py b/py/server/tests/test_plot/test_selectable_dataset.py index 62a59127157..c8558e51ea1 100644 --- a/py/server/tests/test_plot/test_selectable_dataset.py +++ b/py/server/tests/test_plot/test_selectable_dataset.py @@ -11,10 +11,12 @@ class SelectableDatasetTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_one_click(self): sds = one_click(self.test_table, by=['a', 'b']) diff --git a/py/server/tests/test_pt_proxy.py b/py/server/tests/test_pt_proxy.py index 0be79dcfe22..e8c234fb4e0 100644 --- a/py/server/tests/test_pt_proxy.py +++ b/py/server/tests/test_pt_proxy.py @@ -13,6 +13,7 @@ class PartitionedTableProxyTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv").tail(num_rows=100) self.partitioned_table = self.test_table.partition_by(by=["c"]) self.pt_proxy = self.partitioned_table.proxy() @@ -20,6 +21,7 @@ def setUp(self): def tearDown(self): self.partitioned_table = None self.test_table = None + super().tearDown() def test_target(self): self.assertEqual(self.partitioned_table, self.pt_proxy.target) diff --git a/py/server/tests/test_table.py b/py/server/tests/test_table.py index 59d1e54f7d2..f3220f58d34 100644 --- a/py/server/tests/test_table.py +++ b/py/server/tests/test_table.py @@ -13,15 +13,17 @@ from deephaven.html import to_html from deephaven.pandas import to_pandas from deephaven.table import Table -from tests.testbase import BaseTestCase +from tests.testbase import BaseTestCase, make_user_exec_ctx class TableTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_repr(self): regex = r"deephaven\.table\.Table\(io\.deephaven\.engine\.table\.Table\(objectRef=0x.+\{.+\}\)\)" @@ -624,41 +626,24 @@ def closure_fn() -> str: inner_func("param str") def test_nested_scopes(self): - _JExecutionContext = jpy.get_type("io.deephaven.engine.context.ExecutionContext") - context = (_JExecutionContext.newBuilder() - .captureQueryCompiler() - .captureQueryLibrary() - .captureQueryScope() - .build()) - def inner_func(p) -> str: - openContext = context.open() t = empty_table(1).update("X = p * 10") - openContext.close() return t.to_string().split()[2] - t = empty_table(1).update("X = i").update("TableString = inner_func(X + 10)") + with make_user_exec_ctx(): + t = empty_table(1).update("X = i").update("TableString = inner_func(X + 10)") + self.assertIn("100", t.to_string()) def test_nested_scope_ticking(self): - import jpy - _JExecutionContext = jpy.get_type("io.deephaven.engine.context.ExecutionContext") - j_context = (_JExecutionContext.newBuilder() - .captureQueryCompiler() - .captureQueryLibrary() - .captureQueryScope() - .build()) - def inner_func(p) -> str: - open_ctx = j_context.open() t = empty_table(1).update("X = p * 10") - open_ctx.close() return t.to_string().split()[2] - with ugp.shared_lock(): + with make_user_exec_ctx(), ugp.shared_lock(): t = time_table("00:00:01").update("X = i").update("TableString = inner_func(X + 10)") - self.wait_ticking_table_update(t, row_count=5, timeout=10) + self.wait_ticking_table_update(t, row_count=5, timeout=10) self.assertIn("100", t.to_string()) def test_scope_comprehensions(self): diff --git a/py/server/tests/test_table_factory.py b/py/server/tests/test_table_factory.py index f4927f4c91a..b78b1e40894 100644 --- a/py/server/tests/test_table_factory.py +++ b/py/server/tests/test_table_factory.py @@ -26,10 +26,12 @@ class CustomClass: class TableFactoryTestCase(BaseTestCase): def setUp(self): + super().setUp() self.test_table = read_csv("tests/data/test_table.csv") def tearDown(self) -> None: self.test_table = None + super().tearDown() def test_empty_table(self): t = empty_table(10) diff --git a/py/server/tests/test_table_listener.py b/py/server/tests/test_table_listener.py index 0cdd8b276ee..27c4b623aef 100644 --- a/py/server/tests/test_table_listener.py +++ b/py/server/tests/test_table_listener.py @@ -58,6 +58,7 @@ def ensure_ugp_cycles(table_update_recorder: TableUpdateRecorder, cycles: int = class TableListenerTestCase(BaseTestCase): def setUp(self) -> None: + super().setUp() with exclusive_lock(): self.test_table = time_table("00:00:00.001").update(["X=i%11"]).sort("X").tail(16) source_table = time_table("00:00:00.001").update(["TS=currentTime()"]) @@ -66,6 +67,7 @@ def setUp(self) -> None: def tearDown(self) -> None: self.test_table = None self.test_table2 = None + super().tearDown() def check_update_recorder(self, table_update_recorder: TableUpdateRecorder, cols: Union[str, List[str]] = None, *, has_replay: bool = False, has_added: bool = False, diff --git a/py/server/tests/test_ugp.py b/py/server/tests/test_ugp.py index b49b85975e7..a2764a51bbc 100644 --- a/py/server/tests/test_ugp.py +++ b/py/server/tests/test_ugp.py @@ -21,10 +21,12 @@ def partitioned_transform_func(t: Table, ot: Table) -> Table: class UgpTestCase(BaseTestCase): def setUp(self) -> None: + super().setUp() ugp.auto_locking = False def tearDown(self): ugp.auto_locking = False + super().tearDown() def test_ugp_context_manager(self): with self.assertRaises(DHError) as cm: @@ -207,14 +209,6 @@ def test_auto_locking_partitioned_table(self): test_table = time_table("00:00:00.001").update(["X=i", "Y=i%13", "Z=X*Y"]) pt = test_table.partition_by(by="Y") - _ExecutionContext = jpy.get_type("io.deephaven.engine.context.ExecutionContext") - _context = _ExecutionContext.newBuilder() \ - .captureQueryCompiler() \ - .captureQueryLibrary() \ - .emptyQueryScope() \ - .build() \ - .open() - with self.subTest("Merge"): ugp.auto_locking = False with self.assertRaises(DHError) as cm: @@ -242,7 +236,6 @@ def test_auto_locking_partitioned_table(self): ugp.auto_locking = True pt2 = pt.partitioned_transform(pt1, partitioned_transform_func) - _context.close() def test_auto_locking_table_factory(self): with ugp.shared_lock(): diff --git a/py/server/tests/test_updateby.py b/py/server/tests/test_updateby.py index 9c7f48eb9f0..98ae9f43e61 100644 --- a/py/server/tests/test_updateby.py +++ b/py/server/tests/test_updateby.py @@ -12,6 +12,7 @@ class UpdateByTestCase(BaseTestCase): def setUp(self): + super().setUp() self.static_table = read_csv("tests/data/test_table.csv").update("Timestamp=currentTime()") with ugp.exclusive_lock(): self.ticking_table = time_table("00:00:00.001").update( @@ -20,6 +21,7 @@ def setUp(self): def tearDown(self) -> None: self.static_table = None self.ticking_table = None + super().tearDown() def test_ema(self): op_ctrl = OperationControl(on_null=BadDataBehavior.THROW, diff --git a/py/server/tests/testbase.py b/py/server/tests/testbase.py index c9c4641afc4..a42976016eb 100644 --- a/py/server/tests/testbase.py +++ b/py/server/tests/testbase.py @@ -12,8 +12,10 @@ from deephaven.ugp import exclusive_lock from deephaven.table import Table -_JTableTools = jpy.get_type("io.deephaven.engine.util.TableTools") +from test_helper import py_dh_session +_JTableTools = jpy.get_type("io.deephaven.engine.util.TableTools") +_JExecutionContext = jpy.get_type("io.deephaven.engine.context.ExecutionContext") def table_equals(table_a: Table, table_b: Table) -> bool: try: @@ -22,6 +24,18 @@ def table_equals(table_a: Table, table_b: Table) -> bool: raise DHError(e, "table equality test failed.") from e +# TODO(deephaven-core#2867): Wrap ExecutionContext in Python +# Jianfeng to remove this and introduce into deephaven execution_context module for users' convenience +@contextlib.contextmanager +def make_user_exec_ctx(): + j_execution_context = _JExecutionContext.makeExecutionContext(False).open() + try: + yield + except Exception as e: + raise DHError(e, "exception raised in the enclosed code block.") from e + finally: + j_execution_context.close() + class BaseTestCase(unittest.TestCase): @classmethod def setUpClass(cls) -> None: @@ -32,10 +46,10 @@ def tearDownClass(cls) -> None: ... def setUp(self) -> None: - ... + self._execution_context = py_dh_session.getExecutionContext().open() def tearDown(self) -> None: - ... + self._execution_context.close() def wait_ticking_table_update(self, table: Table, row_count: int, timeout: int): """Waits for a ticking table to grow to the specified size or times out.