From 433e91737720abda9f92d9ed2e32be14f0bfade0 Mon Sep 17 00:00:00 2001 From: "James A. Bednar" Date: Fri, 25 Aug 2023 18:27:01 -0500 Subject: [PATCH 01/17] Expanded range info shown in HTML repr --- param/parameterized.py | 65 +++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index e97336121..20ba0f4e7 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -17,6 +17,7 @@ import operator import typing import warnings +import html # Allow this file to be used standalone if desired, albeit without JSON serialization try: @@ -3573,7 +3574,13 @@ def _name_if_set(parameterized): return '' if default_name else parameterized.name -def _get_param_repr(key, val, p, truncate=40): +def truncate(str_, maxlen = 30): + """Return HTML-safe truncated version of given string""" + rep = (str_[:(maxlen-2)] + '..') if (len(str_) > (maxlen-2)) else str_ + return html.escape(rep) + + +def _get_param_repr(key, val, p, vallen=30, doclen=40): """HTML representation for a single Parameter object and its value""" if hasattr(val, "_repr_html_"): try: @@ -3581,31 +3588,48 @@ def _get_param_repr(key, val, p, truncate=40): except: value = val._repr_html_() else: - rep = repr(val) - value = (rep[:truncate] + '..') if len(rep) > truncate else rep + value = truncate(repr(val), vallen) - modes = [] - if p.constant: - modes.append('constant') - if p.readonly: - modes.append('read-only') - if getattr(p, 'allow_None', False): - modes.append('nullable') - mode = ' | '.join(modes) if hasattr(p, 'bounds'): - bounds = p.bounds + if p.bounds is None: + range_ = '(-∞,∞)' + elif hasattr(p,'inclusive_bounds'): + # Numeric bounds use ( and [ to indicate exclusive and inclusive + bl,bu = p.bounds + il,iu = p.inclusive_bounds + lb = ('[' if il else '(') + ('-∞' if bl is None else str(bl)) + ub = ('∞' if bu is None else str(bu)) + (']' if iu else ')') + range_ = lb + ', ' + ub + else: + range_ = repr(p.bounds) elif hasattr(p, 'objects') and p.objects: - bounds = ', '.join(list(map(repr, p.objects))) + range_ = ', '.join(list(map(repr, p.objects))) + elif hasattr(p, 'class_'): + range_ = p.class_.__name__ + elif hasattr(p, 'regex'): + range_ = '.*' if p.regex is None else str(p.regex) else: - bounds = '' + range_ = '' + + if p.readonly: + range_ = ' '.join(['read-only', range_]) + elif p.constant: + range_ = ' '.join(['constant', range_]) + + if getattr(p, 'allow_None', False): + range_ = ' | '.join(['None', range_]) + tooltip = f' class="param-doc-tooltip" data-tooltip="{escape(p.doc.strip())}"' if p.doc else '' + + doc = "" if p.doc is None else truncate(p.doc.strip(), doclen) + return ( f'' - f' {key}' + f' {key}

' + f' {value}' f' {p.__class__.__name__}' - f' {value}' - f' {bounds}' - f' {mode}' + f' {range_}' + f' {doc}

' f'\n' ) @@ -3652,8 +3676,9 @@ def _parameterized_repr_html(p, open): } """ openstr = " open" if open else "" + param_values = p.param.get_param_values() contents = "".join(_get_param_repr(key, val, p.param.params(key)) - for key, val in p.param.get_param_values()) + for key, val in param_values) return ( f'\n' f'
\n' @@ -3662,7 +3687,7 @@ def _parameterized_repr_html(p, open): ' \n' '
\n' ' \n' - f' \n' + f' \n' f'{contents}\n' '
NameType{value_field}Bounds/ObjectsMode
Name{value_field}TypeRangeDoc
\n
\n
\n' ) From bcb8a4c4a6479dcd70e59778ef3d7045b806adae Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 28 Aug 2023 12:56:53 +0200 Subject: [PATCH 02/17] replace deprecated API --- param/parameterized.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 20ba0f4e7..6b48fa5fc 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3676,8 +3676,8 @@ def _parameterized_repr_html(p, open): } """ openstr = " open" if open else "" - param_values = p.param.get_param_values() - contents = "".join(_get_param_repr(key, val, p.param.params(key)) + param_values = p.param.values().items() + contents = "".join(_get_param_repr(key, val, p.param[key]) for key, val in param_values) return ( f'\n' From 600c7b28fe79be0cf408d615606374b86dd5ce49 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 28 Aug 2023 12:57:52 +0200 Subject: [PATCH 03/17] handle class_ as tuple --- param/parameterized.py | 5 ++++- tests/testreprhtml.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/testreprhtml.py diff --git a/param/parameterized.py b/param/parameterized.py index 6b48fa5fc..0fb264eb2 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3605,7 +3605,10 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): elif hasattr(p, 'objects') and p.objects: range_ = ', '.join(list(map(repr, p.objects))) elif hasattr(p, 'class_'): - range_ = p.class_.__name__ + if isinstance(p.class_, tuple): + range_ = ' | '.join(kls.__name__ for kls in p.class_) + else: + range_ = p.class_.__name__ elif hasattr(p, 'regex'): range_ = '.*' if p.regex is None else str(p.regex) else: diff --git a/tests/testreprhtml.py b/tests/testreprhtml.py new file mode 100644 index 000000000..a2ec794d4 --- /dev/null +++ b/tests/testreprhtml.py @@ -0,0 +1,11 @@ +import param + +from param.parameterized import _parameterized_repr_html + + +def test_repr_html_ClassSelector_tuple(): + class P(param.Parameterized): + c = param.ClassSelector(class_=(str, int)) + + rhtml = _parameterized_repr_html(P, True) + assert 'None | str | int' in rhtml From c4e90dd40671d6c833a0148d53b3e343b9915516 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:22:07 +0200 Subject: [PATCH 04/17] refactor tests --- tests/testparameterizedrepr.py | 45 ----------------------------- tests/testreprhtml.py | 53 ++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/tests/testparameterizedrepr.py b/tests/testparameterizedrepr.py index 470914c94..c5c01b528 100644 --- a/tests/testparameterizedrepr.py +++ b/tests/testparameterizedrepr.py @@ -252,48 +252,3 @@ class T(P): pass ) assert t.param.pprint() == 'T()' - - -# HTML repr - -class TestHTMLRepr: - - @pytest.fixture - def Sub(self): - class Sub(param.Parameterized): - xsub = param.Parameter() - - return Sub - - @pytest.fixture - def P(self, Sub): - class P(param.Parameterized): - constant = param.Parameter(constant=True) - readonly = param.Parameter(readonly=True) - allow_None = param.Parameter(1, allow_None=False) - bounds = param.Number(bounds=(-10, 10)) - objects_list = param.Selector(objects=[1, 2]) - objects_dict = param.Selector(objects=dict(a=1, b=2)) - sub = param.ClassSelector(default=Sub(xsub=1), class_=Sub) - - return P - - def test_repr_in_place(self): - assert hasattr(param.Parameterized.param, '_repr_html_') - assert hasattr(param.Parameterized().param, '_repr_html_') - - def test_html_repr_class_str(self, P): - html = P.param._repr_html_() - assert isinstance(html, str) - - def test_html_repr_inst_str(self, P): - html = P().param._repr_html_() - assert isinstance(html, str) - - def test_html_repr_class_sub(self, P): - html = P.param._repr_html_() - assert '
\n \n Sub' in html - - def test_html_repr_inst_sub(self, P): - html = P().param._repr_html_() - assert '
\n \n Sub' in html diff --git a/tests/testreprhtml.py b/tests/testreprhtml.py index a2ec794d4..a256f9dcf 100644 --- a/tests/testreprhtml.py +++ b/tests/testreprhtml.py @@ -1,11 +1,54 @@ import param +import pytest from param.parameterized import _parameterized_repr_html -def test_repr_html_ClassSelector_tuple(): - class P(param.Parameterized): - c = param.ClassSelector(class_=(str, int)) +class TestHTMLRepr: - rhtml = _parameterized_repr_html(P, True) - assert 'None | str | int' in rhtml + @pytest.fixture + def Sub(self): + class Sub(param.Parameterized): + xsub = param.Parameter() + + return Sub + + @pytest.fixture + def P(self, Sub): + class P(param.Parameterized): + constant = param.Parameter(constant=True) + readonly = param.Parameter(readonly=True) + allow_None = param.Parameter(1, allow_None=False) + bounds = param.Number(bounds=(-10, 10)) + objects_list = param.Selector(objects=[1, 2]) + objects_dict = param.Selector(objects=dict(a=1, b=2)) + sub = param.ClassSelector(default=Sub(xsub=1), class_=Sub) + + return P + + def test_repr_in_place(self): + assert hasattr(param.Parameterized.param, '_repr_html_') + assert hasattr(param.Parameterized().param, '_repr_html_') + + def test_html_repr_class_str(self, P): + html = P.param._repr_html_() + assert isinstance(html, str) + + def test_html_repr_inst_str(self, P): + html = P().param._repr_html_() + assert isinstance(html, str) + + def test_html_repr_class_sub(self, P): + html = P.param._repr_html_() + assert '
\n \n Sub' in html + + def test_html_repr_inst_sub(self, P): + html = P().param._repr_html_() + assert '
\n \n Sub' in html + + def test_repr_html_ClassSelector_tuple(self): + class P(param.Parameterized): + c = param.ClassSelector(class_=(str, int)) + + rhtml = _parameterized_repr_html(P, True) + assert 'None | str | int' in rhtml From d1cfbf86439117e41cccd7645f4c1a8826d3e409 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:22:45 +0200 Subject: [PATCH 05/17] remove name from title --- param/parameterized.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 9c7f1ef22..3cd620b42 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3613,13 +3613,6 @@ def type_script_repr(type_,imports,prefix,settings): dbprint_prefix=None -def _name_if_set(parameterized): - """Return the name of this Parameterized if explicitly set to other than the default""" - class_name = parameterized.__class__.__name__ - default_name = re.match('^'+class_name+'[0-9]+$', parameterized.name) - return '' if default_name else parameterized.name - - def truncate(str_, maxlen = 30): """Return HTML-safe truncated version of given string""" rep = (str_[:(maxlen-2)] + '..') if (len(str_) > (maxlen-2)) else str_ @@ -3686,7 +3679,7 @@ def _parameterized_repr_html(p, open): """HTML representation for a Parameterized object""" if isinstance(p, Parameterized): cls = p.__class__ - title = cls.name + "() " + _name_if_set(p) + title = cls.name + "() " value_field = 'Value' else: cls = p From d7244b5b75652492c801aa75ec0d7927630bc473 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:25:59 +0200 Subject: [PATCH 06/17] small fix and additional tests for the title --- param/parameterized.py | 2 +- tests/testreprhtml.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 3cd620b42..c93e17f7b 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3679,7 +3679,7 @@ def _parameterized_repr_html(p, open): """HTML representation for a Parameterized object""" if isinstance(p, Parameterized): cls = p.__class__ - title = cls.name + "() " + title = cls.name + "()" value_field = 'Value' else: cls = p diff --git a/tests/testreprhtml.py b/tests/testreprhtml.py index a256f9dcf..3a83f72b5 100644 --- a/tests/testreprhtml.py +++ b/tests/testreprhtml.py @@ -46,9 +46,17 @@ def test_html_repr_inst_sub(self, P): html = P().param._repr_html_() assert '
\n \n Sub' in html - def test_repr_html_ClassSelector_tuple(self): + def test_html_repr_ClassSelector_tuple(self): class P(param.Parameterized): c = param.ClassSelector(class_=(str, int)) rhtml = _parameterized_repr_html(P, True) assert 'None | str | int' in rhtml + + def test_html_repr_title_class(self, P): + html = P.param._repr_html_() + assert 'P' in html + + def test_html_repr_title_instance(self, P): + html = P().param._repr_html_() + assert 'P()' in html From 3836e04ac45d99945b6a09adada0902151e65283 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:29:13 +0200 Subject: [PATCH 07/17] remove doc column --- param/parameterized.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index c93e17f7b..bf40980a4 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3662,15 +3662,12 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): tooltip = f' class="param-doc-tooltip" data-tooltip="{escape(p.doc.strip())}"' if p.doc else '' - doc = "" if p.doc is None else truncate(p.doc.strip(), doclen) - return ( f'' f' {key}

' f' {value}' f' {p.__class__.__name__}' f' {range_}' - f' {doc}

' f'\n' ) @@ -3728,7 +3725,7 @@ def _parameterized_repr_html(p, open): '
\n' '
\n' ' \n' - f' \n' + f' \n' f'{contents}\n' '
Name{value_field}TypeRangeDoc
Name{value_field}TypeRange
\n
\n
\n' ) From e7c4d0c678a9110b19c1238a562a3fa372a125b8 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:31:50 +0200 Subject: [PATCH 08/17] align Name header to the left --- param/parameterized.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/param/parameterized.py b/param/parameterized.py index bf40980a4..1dc033bae 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3725,7 +3725,7 @@ def _parameterized_repr_html(p, open): '
\n' '
\n' ' \n' - f' \n' + f' \n' f'{contents}\n' '
Name{value_field}TypeRange
Name{value_field}TypeRange
\n
\n
\n' ) From 9f9830b2513a5c34ed12bf2812c4e8684f110fbd Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:34:25 +0200 Subject: [PATCH 09/17] increase tooltip width --- param/parameterized.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 1dc033bae..58e9b73c3 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3692,15 +3692,14 @@ def _parameterized_repr_html(p, open): background-color: black; color: #fff; text-align: center; - border-radius: 3px; + border-radius: 3px; padding: 10px; position: absolute; z-index: 1; top: -5px; left: 100%; margin-left: 10px; - min-width: 100px; - min-width: 150px; + min-width: 250px; } .param-doc-tooltip:hover:before { content: ""; From af1465daa4f501aa1d6d121d6fa5511b52ad8fbc Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:39:40 +0200 Subject: [PATCH 10/17] remove bottom margin under name --- param/parameterized.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 58e9b73c3..b21c04bf9 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3664,7 +3664,7 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): return ( f'' - f' {key}

' + f'

{key}

' f' {value}' f' {p.__class__.__name__}' f' {range_}' @@ -3724,7 +3724,7 @@ def _parameterized_repr_html(p, open): '
\n' '
\n' ' \n' - f' \n' + f' \n' f'{contents}\n' '
Name{value_field}TypeRange
Name{value_field}TypeRange
\n
\n
\n' ) From 7eaf157635e8d9f6336a83f3cff58c2ed78b03de Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:44:01 +0200 Subject: [PATCH 11/17] don't center the tooltip text --- param/parameterized.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index b21c04bf9..13835a66a 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3691,8 +3691,7 @@ def _parameterized_repr_html(p, open): content: attr(data-tooltip); background-color: black; color: #fff; - text-align: center; - border-radius: 3px; + border-radius: 3px; padding: 10px; position: absolute; z-index: 1; From f67b7105a203b94252975034def85b863613d001 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 09:45:57 +0200 Subject: [PATCH 12/17] help cursor when hovering over the name --- param/parameterized.py | 1 + 1 file changed, 1 insertion(+) diff --git a/param/parameterized.py b/param/parameterized.py index 13835a66a..fb8bc238c 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3686,6 +3686,7 @@ def _parameterized_repr_html(p, open): tooltip_css = """ .param-doc-tooltip{ position: relative; + cursor: help; } .param-doc-tooltip:hover:after{ content: attr(data-tooltip); From 094722c35a73f4ea6943d72e517fe9b6b60b1024 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 10:00:45 +0200 Subject: [PATCH 13/17] avoid joining empty strings --- param/parameterized.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index fb8bc238c..67629ac19 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3653,12 +3653,12 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): range_ = '' if p.readonly: - range_ = ' '.join(['read-only', range_]) + range_ = ' '.join(s for s in ['read-only', range_] if s) elif p.constant: - range_ = ' '.join(['constant', range_]) + range_ = ' '.join(s for s in ['constant', range_] if s) if getattr(p, 'allow_None', False): - range_ = ' | '.join(['None', range_]) + range_ = ' | '.join(s for s in ['None', range_] if s) tooltip = f' class="param-doc-tooltip" data-tooltip="{escape(p.doc.strip())}"' if p.doc else '' From c8ed056eba0a3c1707438cecae1a58e85a31f486 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 10:06:18 +0200 Subject: [PATCH 14/17] show allow_None instead --- param/parameterized.py | 2 +- tests/testreprhtml.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 67629ac19..5c0ca45cd 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3658,7 +3658,7 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): range_ = ' '.join(s for s in ['constant', range_] if s) if getattr(p, 'allow_None', False): - range_ = ' | '.join(s for s in ['None', range_] if s) + range_ = ' '.join(s for s in ['allow_None', range_] if s) tooltip = f' class="param-doc-tooltip" data-tooltip="{escape(p.doc.strip())}"' if p.doc else '' diff --git a/tests/testreprhtml.py b/tests/testreprhtml.py index 3a83f72b5..d989eb0a1 100644 --- a/tests/testreprhtml.py +++ b/tests/testreprhtml.py @@ -51,7 +51,7 @@ class P(param.Parameterized): c = param.ClassSelector(class_=(str, int)) rhtml = _parameterized_repr_html(P, True) - assert 'None | str | int' in rhtml + assert 'str | int' in rhtml def test_html_repr_title_class(self, P): html = P.param._repr_html_() From bf723f6ac6cd4b03c74e0d1fe5b88783e800c9b3 Mon Sep 17 00:00:00 2001 From: maximlt Date: Mon, 18 Sep 2023 10:08:01 +0200 Subject: [PATCH 15/17] explicit regex --- param/parameterized.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/param/parameterized.py b/param/parameterized.py index 5c0ca45cd..1787c7902 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3648,7 +3648,7 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): else: range_ = p.class_.__name__ elif hasattr(p, 'regex'): - range_ = '.*' if p.regex is None else str(p.regex) + range_ = 'regex(.*)' if p.regex is None else f'regex({p.regex})' else: range_ = '' From f0e3a096be6aa79c59333b80bf6682c4e075c8ef Mon Sep 17 00:00:00 2001 From: "James A. Bednar" Date: Fri, 22 Sep 2023 23:46:03 -0500 Subject: [PATCH 16/17] Removed redundant default range indicators --- param/parameterized.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 1787c7902..7fe6fcc64 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3630,7 +3630,7 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): if hasattr(p, 'bounds'): if p.bounds is None: - range_ = '(-∞,∞)' + range_ = '' elif hasattr(p,'inclusive_bounds'): # Numeric bounds use ( and [ to indicate exclusive and inclusive bl,bu = p.bounds @@ -3647,8 +3647,8 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): range_ = ' | '.join(kls.__name__ for kls in p.class_) else: range_ = p.class_.__name__ - elif hasattr(p, 'regex'): - range_ = 'regex(.*)' if p.regex is None else f'regex({p.regex})' + elif hasattr(p, 'regex') and p.regex is not None: + range_ = f'regex({p.regex})' else: range_ = '' @@ -3658,15 +3658,15 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): range_ = ' '.join(s for s in ['constant', range_] if s) if getattr(p, 'allow_None', False): - range_ = ' '.join(s for s in ['allow_None', range_] if s) + range_ = ' '.join(s for s in ['nullable', range_] if s) tooltip = f' class="param-doc-tooltip" data-tooltip="{escape(p.doc.strip())}"' if p.doc else '' return ( f'' f'

{key}

' - f' {value}' - f' {p.__class__.__name__}' + f' {value}' + f' {p.__class__.__name__}' f' {range_}' f'\n' ) @@ -3724,7 +3724,7 @@ def _parameterized_repr_html(p, open): '
\n' '
\n' ' \n' - f' \n' + f' \n' f'{contents}\n' '
Name{value_field}TypeRange
Name{value_field}TypeRange
\n
\n
\n' ) From a289244d50b51d0f0870d40e30d800d31087074b Mon Sep 17 00:00:00 2001 From: "James A. Bednar" Date: Fri, 22 Sep 2023 23:59:29 -0500 Subject: [PATCH 17/17] Switched from mathematical range notation to Python expr --- param/parameterized.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/param/parameterized.py b/param/parameterized.py index 7fe6fcc64..3622cfbc2 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -3635,9 +3635,10 @@ def _get_param_repr(key, val, p, vallen=30, doclen=40): # Numeric bounds use ( and [ to indicate exclusive and inclusive bl,bu = p.bounds il,iu = p.inclusive_bounds - lb = ('[' if il else '(') + ('-∞' if bl is None else str(bl)) - ub = ('∞' if bu is None else str(bu)) + (']' if iu else ')') - range_ = lb + ', ' + ub + + lb = '' if bl is None else ('>=' if il else '>') + str(bl) + ub = '' if bu is None else ('<=' if iu else '<') + str(bu) + range_ = lb + (', ' if lb and bu else '') + ub else: range_ = repr(p.bounds) elif hasattr(p, 'objects') and p.objects: