diff --git a/src/bokeh/plotting/_legends.py b/src/bokeh/plotting/_legends.py index 36d3ab31178..e273d92892f 100644 --- a/src/bokeh/plotting/_legends.py +++ b/src/bokeh/plotting/_legends.py @@ -10,7 +10,10 @@ #----------------------------------------------------------------------------- from __future__ import annotations +from bokeh.util.strings import nice_join + import logging # isort:skip + log = logging.getLogger(__name__) #----------------------------------------------------------------------------- @@ -52,9 +55,14 @@ #----------------------------------------------------------------------------- def pop_legend_kwarg(kwargs: dict[str, Any]) -> tuple[Any, str]: - result = {attr: kwargs.pop(attr) for attr in LEGEND_ARGS if attr in kwargs} - if len(result) > 1: - from ..util.strings import nice_join + num_legend_args = 0 + result = {} + for attr in LEGEND_ARGS: + if attr in kwargs: + result[attr] = kwargs.pop(attr) + num_legend_args += 1 + + if num_legend_args > 1: raise ValueError(f"Only one of {nice_join(LEGEND_ARGS)} may be provided, got: {nice_join(result.keys())}") legend_name = kwargs.pop("legend_name", None) diff --git a/src/bokeh/util/strings.py b/src/bokeh/util/strings.py index 15eb0a62271..c2594dde31e 100644 --- a/src/bokeh/util/strings.py +++ b/src/bokeh/util/strings.py @@ -61,7 +61,7 @@ def indent(text: str, n: int = 2, ch: str = " ") -> str: def nice_join(seq: Iterable[Any], *, sep: str = ", ", conjunction: str | None = "or") -> str: - ''' Join together sequences of strings into English-friendly phrases using + """ Join together sequences of strings into English-friendly phrases using the conjunction ``or`` when appropriate. Args: @@ -77,13 +77,20 @@ def nice_join(seq: Iterable[Any], *, sep: str = ", ", conjunction: str | None = >>> nice_join(["a", "b", "c"]) 'a, b or c' - ''' - seq = [str(x) for x in seq] + """ + # Use map(str, ...) for better performance on string conversion + # Avoid building the list unless more than one element or conjunction is not None + if hasattr(seq, '__len__'): + n = len(seq) + str_seq = list(map(str, seq)) + else: + str_seq = list(map(str, seq)) + n = len(str_seq) - if len(seq) <= 1 or conjunction is None: - return sep.join(seq) + if n <= 1 or conjunction is None: + return sep.join(str_seq) else: - return f"{sep.join(seq[:-1])} {conjunction} {seq[-1]}" + return f"{sep.join(str_seq[:-1])} {conjunction} {str_seq[-1]}" def snakify(name: str, sep: str = "_") -> str: