From f141b34801f5df372abeb79a15d7cc3b2bc64bc4 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Sun, 12 Oct 2025 16:47:33 +0200 Subject: [PATCH] dev: allow columns to be got and set --- CHANGELOG.md | 3 +++ docs/api.rst | 2 +- docs/console-output.md | 9 ++++--- src/structlog/dev.py | 55 +++++++++++++++++++++++++++++++++--------- tests/test_dev.py | 15 ++++++++++++ 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e44725..3a422b20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,9 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/ - Same for *sort_keys*. [#756](https://github.com/hynek/structlog/pull/756) +- Same for *columns*. + [#757](https://github.com/hynek/structlog/pull/757) + - Added `structlog.dev.ConsoleRenderer.get_default_column_styles` for reuse the default column styles. [#741](https://github.com/hynek/structlog/pull/741) diff --git a/docs/api.rst b/docs/api.rst index 8a20b007..a7f30d2a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -76,7 +76,7 @@ API Reference .. automodule:: structlog.dev .. autoclass:: ConsoleRenderer - :members: get_default_level_styles, get_default_column_styles, exception_formatter, sort_keys + :members: get_default_level_styles, get_default_column_styles, exception_formatter, sort_keys, columns .. autoclass:: ColumnStyles diff --git a/docs/console-output.md b/docs/console-output.md index 715b6413..41e21a81 100644 --- a/docs/console-output.md +++ b/docs/console-output.md @@ -99,13 +99,14 @@ cr = structlog.dev.ConsoleRenderer( structlog.configure(processors=structlog.get_config()["processors"][:-1]+[cr]) ``` -:::{hint} -You can replace only the last processor using: +You can also access and configure the columns of the active console renderer: ```python -structlog.configure(processors=structlog.get_config()["processors"][:-1]+[cr]) +cr = structlog.dev.get_active_console_renderer() +cr.columns = [ + ... +] ``` -::: ## Standard environment variables diff --git a/src/structlog/dev.py b/src/structlog/dev.py index af4386a8..dcdd2637 100644 --- a/src/structlog/dev.py +++ b/src/structlog/dev.py @@ -587,6 +587,8 @@ class ConsoleRenderer: .. versionadded:: 24.2.0 *pad_level* """ + _default_column_formatter: ColumnFormatter + def __init__( self, pad_event: int = _EVENT_WIDTH, @@ -636,16 +638,7 @@ def add_meaningless_arg(arg: str) -> None: for w in to_warn: warnings.warn(w, stacklevel=2) - defaults = [col for col in columns if col.key == ""] - if not defaults: - raise ValueError( - "Must pass a default column formatter (a column with `key=''`)." - ) - if len(defaults) > 1: - raise ValueError("Only one default column formatter allowed.") - - self._default_column_formatter = defaults[0].formatter - self._columns = [col for col in columns if col.key] + self.columns = columns return @@ -808,7 +801,7 @@ def __call__( kvs = [ col.formatter(col.key, val) - for col in self._columns + for col in self.columns if (val := event_dict.pop(col.key, _NOTHING)) is not _NOTHING ] + [ self._default_column_formatter(key, event_dict[key]) @@ -901,6 +894,46 @@ def sort_keys(self, value: bool) -> None: """ self._sort_keys = value + @property + def columns(self) -> list[Column]: + """ + The columns configuration for this console renderer. + + Warning: + Just like with passing *columns* argument, many of the other + arguments you may have passed are ignored. + + Args: + value: + A list of `Column` objects defining both the order and format + of the key-value pairs in the output. + + **Must** contain a column with ``key=''`` that defines the + default formatter. + + Raises: + ValueError: If there's not exactly one default column formatter. + + .. versionadded:: 25.5.0 + """ + return [Column("", self._default_column_formatter), *self._columns] + + @columns.setter + def columns(self, value: list[Column]) -> None: + """ + .. versionadded:: 25.5.0 + """ + defaults = [col for col in value if col.key == ""] + if not defaults: + raise ValueError( + "Must pass a default column formatter (a column with `key=''`)." + ) + if len(defaults) > 1: + raise ValueError("Only one default column formatter allowed.") + + self._default_column_formatter = defaults[0].formatter + self._columns = [col for col in value if col.key] + _SENTINEL = object() diff --git a/tests/test_dev.py b/tests/test_dev.py index d108769b..fbd23d02 100644 --- a/tests/test_dev.py +++ b/tests/test_dev.py @@ -709,6 +709,21 @@ def test_sort_keys_property(self, cr): assert cr.sort_keys is True assert cr._sort_keys is True + def test_columns_property(self, cr): + """ + The columns property can be set and retrieved without re-instantiating + ConsoleRenderer. + + The property also fakes the default column formatter. + """ + cols = [dev.Column("", lambda k, v: "")] + + cr.columns = cols + + assert cols == cr.columns + assert [] == cr._columns + assert cols[0].formatter == cr._default_column_formatter + class TestSetExcInfo: def test_wrong_name(self):