From 4071a5f09c085ddc70de49723a6a7ecdd57eaef1 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 2 Oct 2023 18:45:53 +0200 Subject: [PATCH 1/6] Fix resolution of explicit_no_refs --- param/parameterized.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/param/parameterized.py b/param/parameterized.py index 9af617ebe..cebfb041d 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3567,7 +3567,7 @@ def __param_inheritance(mcs, param_name, param): callables[slot] = default_val else: slot_values[slot] = default_val - elif slot == 'allow_refs' and not slot_values[slot]: + elif slot == 'allow_refs' and param.allow_refs is False: # Track Parameters that explicitly declared no refs mcs._param__private.explicit_no_refs.append(param.name) From 7fe1957ba88faa797adb5e057bbf338bba2f2bd1 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 2 Oct 2023 18:46:17 +0200 Subject: [PATCH 2/6] Correctly resolve value on async function --- param/parameterized.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/param/parameterized.py b/param/parameterized.py index cebfb041d..99ee11b30 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -173,7 +173,7 @@ def resolve_value(value): resolve_value(value.step) ) value = transform_reference(value) - if hasattr(value, '_dinfo'): + if hasattr(value, '_dinfo') or iscoroutinefunction(value): value = eval_function_with_deps(value) elif isinstance(value, Parameter): value = getattr(value.owner, value.name) From bb742c2cd5cbfc10310dab81d0e722a5d6f73fb1 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 2 Oct 2023 19:00:05 +0200 Subject: [PATCH 3/6] Add async ref tests --- pyproject.toml | 1 + tests/conftest.py | 16 ++++++++++++++++ tests/testrefs.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 0659c7796..1faac2fa7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ doc = [ tests = [ "coverage[toml]", "pytest", + "pytest-asyncio", ] tests-deser = [ "xlrd", diff --git a/tests/conftest.py b/tests/conftest.py index ca3e41d51..f0fa9f5d3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,17 @@ +import asyncio + import param import pytest param.parameterized.warnings_as_exceptions = True +def async_execute(func): + event_loop = asyncio.get_running_loop() + if event_loop.is_running(): + asyncio.create_task(func()) + else: + event_loop.run_until_complete(func()) + return @pytest.fixture def dataframe(): @@ -12,3 +21,10 @@ def dataframe(): 'float': [3.14, 6.28, 9.42], 'str': ['A', 'B', 'C'] }, index=[1, 2, 3], columns=['int', 'float', 'str']) + +@pytest.fixture +def async_executor(): + old_executor = param.parameterized.async_executor + param.parameterized.async_executor = async_execute + yield + param.parameterized.async_executor = old_executor diff --git a/tests/testrefs.py b/tests/testrefs.py index a1c57110a..ece1063f9 100644 --- a/tests/testrefs.py +++ b/tests/testrefs.py @@ -1,3 +1,5 @@ +import asyncio + import param import pytest @@ -100,3 +102,38 @@ def test_nested_param_method_ref(): assert p2.string == 'string!' p.string = 'new string' assert p2.string == 'new string!' + +async def test_async_function_ref(async_executor): + async def gen_strings(): + await asyncio.sleep(0.02) + return 'string!' + + p = Parameters(string=gen_strings) + + await asyncio.sleep(0.1) + assert p.string == 'string!' + +async def test_async_bind_ref(async_executor): + p = Parameters() + + async def exclaim(string): + await asyncio.sleep(0.05) + return string + '!' + + p2 = Parameters(string=bind(exclaim, p.param.string)) + await asyncio.sleep(0.1) + assert p2.string == 'string!' + p.string = 'new string' + await asyncio.sleep(0.1) + assert p2.string == 'new string!' + +async def test_async_generator_ref(async_executor): + async def gen_strings(): + string = 'string' + for i in range(10): + yield string + '!' * i + + p = Parameters(string=gen_strings) + + await asyncio.sleep(0.1) + assert p.string == 'string!!!!!!!!!' From 592b2d7c177c156a56593e3e71780e857e4ace1e Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 2 Oct 2023 19:27:31 +0200 Subject: [PATCH 4/6] Fix and tests for explicit no refs tracking --- param/parameterized.py | 8 ++++++-- tests/testrefs.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 99ee11b30..485c3989d 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3567,9 +3567,13 @@ def __param_inheritance(mcs, param_name, param): callables[slot] = default_val else: slot_values[slot] = default_val - elif slot == 'allow_refs' and param.allow_refs is False: + elif slot == 'allow_refs': # Track Parameters that explicitly declared no refs - mcs._param__private.explicit_no_refs.append(param.name) + explicit_no_refs = mcs._param__private.explicit_no_refs + if param.allow_refs is False: + explicit_no_refs.append(param.name) + elif param.allow_refs is True and param.name in explicit_no_refs: + explicit_no_refs.remove(param.name) # Now set the actual slot values for slot, value in slot_values.items(): diff --git a/tests/testrefs.py b/tests/testrefs.py index ece1063f9..684003310 100644 --- a/tests/testrefs.py +++ b/tests/testrefs.py @@ -13,6 +13,15 @@ class Parameters(param.Parameterized): string_list = param.List(default=[], item_type=str, allow_refs=True, nested_refs=True) + no_refs = param.Parameter(allow_refs=False) + +class Subclass(Parameters): + + no_refs = param.Parameter() + +class SubclassOverride(Parameters): + + no_refs = param.Parameter(allow_refs=True) class Nested(param.Parameterized): @@ -23,6 +32,15 @@ def string(self): return self.subobject.string + '!' +def test_class_explicit_no_refs(): + assert Parameters._param__private.explicit_no_refs == ['no_refs'] + +def test_subclass_explicit_no_refs(): + assert Subclass._param__private.explicit_no_refs == ['no_refs'] + +def test_subclass_explicit_no_refs_override(): + assert SubclassOverride._param__private.explicit_no_refs == [] + def test_parameterized_warns_explicit_no_ref(): class ImplicitRefsParameters(param.Parameterized): parameter = param.Parameter(default="string") From 07a05e6d915364b77128c285047562eb70923898 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 2 Oct 2023 19:32:42 +0200 Subject: [PATCH 5/6] Configure async tests --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 1faac2fa7..323d798e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -219,6 +219,7 @@ filterwarnings = [ "error", ] xfail_strict = "true" +asyncio_mode = "auto" [tool.coverage.report] omit = ["param/version.py"] From 6f88db6c83d0dc87b6558a8af7a16aa1b87a86b8 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 2 Oct 2023 19:43:44 +0200 Subject: [PATCH 6/6] Require pytest-asyncio for example tests --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 323d798e2..cc17bb806 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ tests-deser = [ ] tests-examples = [ "pytest", + "pytest-asyncio", "pytest-xdist", "nbval", "param[examples]",