diff --git a/param/_utils.py b/param/_utils.py index a64ea027..b4ed1524 100644 --- a/param/_utils.py +++ b/param/_utils.py @@ -546,3 +546,11 @@ def exceptions_summarized(): import sys etype, value, tb = sys.exc_info() print(f"{etype.__name__}: {value}", file=sys.stderr) + + +def _in_ipython(): + try: + get_ipython() + return True + except NameError: + return False diff --git a/param/depends.py b/param/depends.py index 7b16889d..20753657 100644 --- a/param/depends.py +++ b/param/depends.py @@ -1,5 +1,3 @@ -import weakref - from collections import defaultdict from functools import wraps @@ -8,25 +6,6 @@ ) from ._utils import accept_arguments, iscoroutinefunction -_display_accessors = {} -_reactive_display_objs = weakref.WeakSet() - -def register_display_accessor(name, accessor, force=False): - if name in _display_accessors and not force: - raise KeyError( - 'Display accessor {name!r} already registered. Override it ' - 'by setting force=True or unregister the existing accessor first.') - _display_accessors[name] = accessor - for fn in _reactive_display_objs: - setattr(fn, name, accessor(fn)) - -def unregister_display_accessor(name): - if name not in _display_accessors: - raise KeyError('No such display accessor: {name!r}') - del _display_accessors[name] - for fn in _reactive_display_objs: - delattr(fn, name) - @accept_arguments def depends(func, *dependencies, watch=False, on_init=False, **kw): diff --git a/param/display.py b/param/display.py new file mode 100644 index 00000000..3b05fd21 --- /dev/null +++ b/param/display.py @@ -0,0 +1,20 @@ +import weakref + +_display_accessors = {} +_reactive_display_objs = weakref.WeakSet() + +def register_display_accessor(name, accessor, force=False): + if name in _display_accessors and not force: + raise KeyError( + 'Display accessor {name!r} already registered. Override it ' + 'by setting force=True or unregister the existing accessor first.') + _display_accessors[name] = accessor + for fn in _reactive_display_objs: + setattr(fn, name, accessor(fn)) + +def unregister_display_accessor(name): + if name not in _display_accessors: + raise KeyError('No such display accessor: {name!r}') + del _display_accessors[name] + for fn in _reactive_display_objs: + delattr(fn, name) diff --git a/param/ipython.py b/param/ipython.py index 32baf904..72f9e7fa 100644 --- a/param/ipython.py +++ b/param/ipython.py @@ -24,9 +24,7 @@ import param -from param.depends import depends, register_display_accessor -from param.parameterized import resolve_ref -from param.reactive import rx +from param.display import register_display_accessor # Whether to generate warnings when misformatted docstrings are found @@ -365,6 +363,10 @@ def __init__(self, reactive): self._reactive = reactive def __call__(self): + from param.depends import depends + from param.parameterized import resolve_ref + from param.reactive import rx + if isinstance(self._reactive, rx): cb = self._reactive._callback @depends(*self._reactive._params, watch=True) diff --git a/param/parameterized.py b/param/parameterized.py index 654f6667..68ad65c6 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -45,6 +45,7 @@ _deprecated, _deprecate_positional_args, _dict_update, + _in_ipython, _is_auto_name, _is_mutable_container, _recursive_repr, @@ -54,17 +55,19 @@ descendents, ) -try: - get_ipython() -except NameError: - param_pager = None -else: +# Ideally setting param_pager would be in __init__.py but param_pager is +# needed on import to create the Parameterized class, so it'd need to precede +# importing parameterized.py in __init__.py which would be a little weird. +if _in_ipython(): # In case the optional ipython module is unavailable try: from .ipython import ParamPager param_pager = ParamPager(metaclass=True) # Generates param description - except: + except ImportError: param_pager = None +else: + param_pager = None + from inspect import getfullargspec diff --git a/param/reactive.py b/param/reactive.py index 5efd9856..eca14f6d 100644 --- a/param/reactive.py +++ b/param/reactive.py @@ -90,9 +90,8 @@ from typing import Any, Callable, Optional from . import Event -from .depends import ( - _display_accessors, _reactive_display_objs, depends, -) +from .depends import depends +from .display import _display_accessors, _reactive_display_objs from .parameterized import ( Parameter, Parameterized, eval_function_with_deps, get_method_owner, register_reference_transform, resolve_ref, resolve_value, transform_reference diff --git a/tests/testipythonmagic.py b/tests/testipythonmagic.py index 9320fa0e..2a1e68da 100644 --- a/tests/testipythonmagic.py +++ b/tests/testipythonmagic.py @@ -14,9 +14,6 @@ if os.getenv('PARAM_TEST_IPYTHON','0') == '1': raise ImportError("PARAM_TEST_IPYTHON=1 but ipython not available.") -# TODO: is the below actually true? - -# SkipTest will be raised if IPython unavailable from param.ipython import ParamPager test1_repr = """\x1b[1;32mParameters of 'TestClass'\n=========================\n\x1b[0m\n\x1b[1;31mParameters changed from their default values are marked in red.\x1b[0m\n\x1b[1;36mSoft bound values are marked in cyan.\x1b[0m\nC/V= Constant/Variable, RO/RW = ReadOnly/ReadWrite, AN=Allow None\n\n\x1b[1;34mNameValue Type Bounds Mode \x1b[0m\n\nu 4 Number V RW \nw 4 Number C RO \nv 4 Number C RW \nx None String V RW AN \ny 4 Number (-1, None) V RW \nz 4 Number (-1, 100) V RW \n\n\x1b[1;32mParameter docstrings:\n=====================\x1b[0m\n\n\x1b[1;34mu: < No docstring available >\x1b[0m\n\x1b[1;31mw: < No docstring available >\x1b[0m\n\x1b[1;34mv: < No docstring available >\x1b[0m\n\x1b[1;31mx: < No docstring available >\x1b[0m\n\x1b[1;34my: < No docstring available >\x1b[0m\n\x1b[1;31mz: < No docstring available >\x1b[0m"""