Display
coola.display ¶
Provide helpers to display objects and to write to the terminal.
The package has three kinds of content:
- Mixins:
InlineDisplayMixin,MultilineDisplayMixinandBaseDisplayMixingive a class a consistent__repr__and__str__. A class only overrides_display_fields. - Pydantic formatting:
str_pydantic_modelandrepr_pydantic_modelformat a pydantic model without exposing its secrets. They needpydantic, which is imported the first time they are used. - Terminal output:
coola.display.richandcoola.display.colorlogconfigure logging and print withrichandcolorlog. A function raisesMissingPackageErrorwhen its library is not installed.
coola.display.BaseDisplayMixin ¶
Bases: ABC
Abstract base for display mixins.
__repr__ and __str__ are defined here: they get the fields
from _display_fields and delegate the layout to
_render_fields. Subclasses must implement _render_fields to
choose the layout. Classes that use a layout mixin override
_display_fields to return the values to show; by default no
field is shown.
coola.display.InlineDisplayMixin ¶
Bases: BaseDisplayMixin
Mixin that renders __repr__ and __str__ on a single line.
All fields are displayed inline. Best suited for simple objects
with few, short fields where a compact single-line format aids
readability. Override _display_fields to choose the fields to
show; by default none is shown, which suits stateless classes.
Example
>>> from coola.display import InlineDisplayMixin
>>> from typing import Any
>>> class MyClass(InlineDisplayMixin):
... def __init__(self, key1: str, key2: str) -> None:
... self.key1 = key1
... self.key2 = key2
... def _display_fields(self) -> dict[str, Any]:
... return {"key1": self.key1, "key2": self.key2}
...
>>> obj = MyClass(key1="value1", key2="value2")
>>> print(repr(obj))
MyClass(key1='value1', key2='value2')
>>> print(str(obj))
MyClass(key1=value1, key2=value2)
>>> class NoFields(InlineDisplayMixin):
... pass
...
>>> print(repr(NoFields()))
NoFields()
coola.display.MultilineDisplayMixin ¶
Bases: BaseDisplayMixin
Mixin that renders __repr__ and __str__ in multiline dict
format.
Each field is displayed on its own indented line. Best suited for
objects with several fields or deeply nested values where a
single-line format would be hard to read. Override
_display_fields to choose the fields to show; by default none
is shown.
Example
>>> from coola.display import MultilineDisplayMixin
>>> from typing import Any
>>> class MyClass(MultilineDisplayMixin):
... def __init__(self, key1: str, key2: str) -> None:
... self.key1 = key1
... self.key2 = key2
... def _display_fields(self) -> dict[str, Any]:
... return {"key1": self.key1, "key2": self.key2}
...
>>> obj = MyClass(key1="value1", key2="value2")
>>> print(repr(obj))
MyClass(
(key1): value1
(key2): value2
)
>>> print(str(obj))
MyClass(
(key1): value1
(key2): value2
)
coola.display.repr_pydantic_model ¶
repr_pydantic_model(
model: BaseModel,
*,
sort: bool = True,
exclude_none: bool = False,
exclude_secret: bool = True,
exclude_fields: list[str] | None = None,
) -> str
Return a formatted, single-line repr-style representation of
a pydantic model.
It has the same options and behaviour as
:func:str_pydantic_model, but it uses repr for each value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
BaseModel
|
The pydantic model to format. |
required |
sort
|
bool
|
See :func: |
True
|
exclude_none
|
bool
|
See :func: |
False
|
exclude_secret
|
bool
|
See :func: |
True
|
exclude_fields
|
list[str] | None
|
See :func: |
None
|
Returns:
| Type | Description |
|---|---|
str
|
A string like |
Example
>>> from pydantic import BaseModel
>>> from coola.display.pydantic import repr_pydantic_model
>>> class Config(BaseModel):
... name: str
... count: int
...
>>> repr_pydantic_model(Config(name="my-app", count=3))
"Config(count=3, name='my-app')"
coola.display.str_pydantic_model ¶
str_pydantic_model(
model: BaseModel,
*,
sort: bool = True,
exclude_none: bool = False,
exclude_secret: bool = True,
exclude_fields: list[str] | None = None,
) -> str
Return a formatted, single-line string representation of a pydantic model.
The fields are read from the model: no serializer runs, a field
declared with Field(repr=False) is hidden (as in the repr of
pydantic), the values of extra="allow" are shown, and a
computed_field is not shown because it is not a field. A nested
model keeps its name and is formatted with sort,
exclude_none and exclude_secret (also inside lists, tuples,
sets, frozensets and dicts). The nested secrets are never shown:
they are omitted, or masked by pydantic if exclude_secret is
False.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
BaseModel
|
The pydantic model to format. |
required |
sort
|
bool
|
If |
True
|
exclude_none
|
bool
|
If |
False
|
exclude_secret
|
bool
|
If |
True
|
exclude_fields
|
list[str] | None
|
Optional list of field names to omit. A
|
None
|
Returns:
| Type | Description |
|---|---|
str
|
A string like |
Example
>>> from pydantic import BaseModel
>>> from coola.display.pydantic import str_pydantic_model
>>> class Child(BaseModel):
... name: str
...
>>> class Config(BaseModel):
... name: str
... count: int
... child: Child
...
>>> str_pydantic_model(Config(name="my-app", count=3, child=Child(name="a")))
'Config(child=Child(name=a), count=3, name=my-app)'
coola.display.pydantic ¶
Contain utilities to generate formatted strings of pydantic objects.
coola.display.pydantic.is_secret_annotation ¶
is_secret_annotation(annotation: object) -> bool
Indicate if an annotation is, or contains, a secret type.
The secret types are SecretStr, SecretBytes and Secret.
The arguments of generic aliases (Optional, tuple,
Annotated, ...) are inspected recursively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
annotation
|
object
|
The type annotation to inspect. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Example
>>> from typing import Optional
>>> from pydantic import SecretStr
>>> from coola.display.pydantic import is_secret_annotation
>>> is_secret_annotation(Optional[SecretStr])
True
>>> is_secret_annotation(int)
False
coola.display.pydantic.repr_pydantic_model ¶
repr_pydantic_model(
model: BaseModel,
*,
sort: bool = True,
exclude_none: bool = False,
exclude_secret: bool = True,
exclude_fields: list[str] | None = None,
) -> str
Return a formatted, single-line repr-style representation of
a pydantic model.
It has the same options and behaviour as
:func:str_pydantic_model, but it uses repr for each value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
BaseModel
|
The pydantic model to format. |
required |
sort
|
bool
|
See :func: |
True
|
exclude_none
|
bool
|
See :func: |
False
|
exclude_secret
|
bool
|
See :func: |
True
|
exclude_fields
|
list[str] | None
|
See :func: |
None
|
Returns:
| Type | Description |
|---|---|
str
|
A string like |
Example
>>> from pydantic import BaseModel
>>> from coola.display.pydantic import repr_pydantic_model
>>> class Config(BaseModel):
... name: str
... count: int
...
>>> repr_pydantic_model(Config(name="my-app", count=3))
"Config(count=3, name='my-app')"
coola.display.pydantic.secret_field_names ¶
secret_field_names(model: BaseModel) -> set[str]
Return the names of fields annotated as a secret type.
A field is secret if its annotation is SecretStr,
SecretBytes or Secret, or contains one of them (for example
Optional[SecretStr] or tuple[SecretStr, int]). The whole
field is then considered secret.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
BaseModel
|
The pydantic model to inspect. |
required |
Returns:
| Type | Description |
|---|---|
set[str]
|
The set of field names whose annotation is (or includes) a secret type. |
Example
>>> from pydantic import BaseModel, SecretStr
>>> from coola.display.pydantic import secret_field_names
>>> class Config(BaseModel):
... name: str
... token: SecretStr
...
>>> secret_field_names(Config(name="my-app", token="s3cr3t"))
{'token'}
coola.display.pydantic.str_pydantic_model ¶
str_pydantic_model(
model: BaseModel,
*,
sort: bool = True,
exclude_none: bool = False,
exclude_secret: bool = True,
exclude_fields: list[str] | None = None,
) -> str
Return a formatted, single-line string representation of a pydantic model.
The fields are read from the model: no serializer runs, a field
declared with Field(repr=False) is hidden (as in the repr of
pydantic), the values of extra="allow" are shown, and a
computed_field is not shown because it is not a field. A nested
model keeps its name and is formatted with sort,
exclude_none and exclude_secret (also inside lists, tuples,
sets, frozensets and dicts). The nested secrets are never shown:
they are omitted, or masked by pydantic if exclude_secret is
False.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
BaseModel
|
The pydantic model to format. |
required |
sort
|
bool
|
If |
True
|
exclude_none
|
bool
|
If |
False
|
exclude_secret
|
bool
|
If |
True
|
exclude_fields
|
list[str] | None
|
Optional list of field names to omit. A
|
None
|
Returns:
| Type | Description |
|---|---|
str
|
A string like |
Example
>>> from pydantic import BaseModel
>>> from coola.display.pydantic import str_pydantic_model
>>> class Child(BaseModel):
... name: str
...
>>> class Config(BaseModel):
... name: str
... count: int
... child: Child
...
>>> str_pydantic_model(Config(name="my-app", count=3, child=Child(name="a")))
'Config(child=Child(name=a), count=3, name=my-app)'
coola.display.colorlog ¶
Provide utilities for configuring Python's logging output.
coola.display.colorlog.configure_colorlog_logging ¶
configure_colorlog_logging(
level: int = INFO, *, force: bool = False, **kwargs: Any
) -> None
Configure the root logger, using a coloured formatter when available.
If the colorlog package is installed and sys.stderr is
attached to a terminal, attaches a colorlog.StreamHandler
with per-level colours for both the log metadata (level, logger
name, line number) and the message itself. If colorlog is not
installed, or output is not a terminal (e.g. redirected to a file
or running in CI), falls back to plain logging.basicConfig
with no formatting, to avoid emitting raw ANSI escape codes into
non-interactive output.
The coloured handler writes to stream if given, and to
sys.stderr otherwise; the terminal check applies to this
stream. If handlers, filename, filemode, format or
style is given, the caller controls the output and the call is
forwarded to logging.basicConfig without a coloured
handler. datefmt is used by the coloured formatter.
colorlog is optional, unlike rich for
:func:~coola.display.rich.configure_rich_logging: without it the
output has no colour, which is a correct result for a logging
helper, so no error is raised.
Note
logging.basicConfig is a no-op if the root logger
already has handlers configured. Pass force=True to remove
existing handlers and reconfigure unconditionally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
int
|
Minimum log level for the root logger. Accepts any
constant from |
INFO
|
force
|
bool
|
When |
False
|
**kwargs
|
Any
|
Additional keyword arguments passed to
|
{}
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If |
Example
>>> import logging
>>> from coola.display.colorlog import configure_colorlog_logging
>>> configure_colorlog_logging(level=logging.DEBUG, force=True)
>>> logging.getLogger().level
10
coola.display.rich ¶
Common display utilities for rich.
coola.display.rich.configure_rich_logging ¶
configure_rich_logging(
level: int = INFO,
*,
fmt: str = "%(message)s",
datefmt: str = "[%Y-%m-%d %H:%M:%S]",
rich_tracebacks: bool = True,
markup: bool = True,
force: bool = False,
console: Console | None = None,
**kwargs: Any,
) -> None
Configure the root logger to use
:class:rich.logging.RichHandler.
Sets up Python's standard :mod:logging module with a single
:class:~rich.logging.RichHandler so that all log output is rendered
through Rich's console, with coloured levels, timestamps, and optional
pretty-printed tracebacks.
After calling this function, obtain a logger for the current module with the standard idiom::
logger = logging.getLogger(__name__)
Using :func:logging.getLogger with __name__ gives the logger a
dotted name matching its import path (e.g. my_package.my_module),
which slots it correctly into the logging hierarchy and allows callers to
tune or silence it by name.
Note
:func:logging.basicConfig is a no-op if the root logger already
has handlers configured (e.g. if another library called
basicConfig first, or if you call this function more than once).
Pass force=True to remove any existing handlers and reconfigure
unconditionally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
int
|
Minimum log level for the root logger. Accepts any constant
from :mod: |
INFO
|
fmt
|
str
|
Log record format string passed to :class: |
'%(message)s'
|
datefmt
|
str
|
Date/time format string for the |
'[%Y-%m-%d %H:%M:%S]'
|
rich_tracebacks
|
bool
|
When |
True
|
markup
|
bool
|
When |
True
|
force
|
bool
|
When |
False
|
console
|
Console | None
|
The :class: |
None
|
**kwargs
|
Any
|
Additional keyword arguments forwarded verbatim to
:class: See the |
{}
|
Raises:
| Type | Description |
|---|---|
MissingPackageError
|
If |
TypeError
|
If an unrecognised keyword argument is passed that
:class: |
Example
>>> import logging
>>> from coola.display.rich import configure_rich_logging
>>> configure_rich_logging()
>>> logger = logging.getLogger(__name__)
>>> logger.info("Rich logging is active")
coola.display.rich.make_progressbar ¶
make_progressbar(
*,
transient: bool = False,
console: Console | None = None,
) -> Progress
Create a Rich progress bar with a standard column layout.
Builds a Progress instance with a consistent column layout:
- A spinner indicating the task is active.
- Description text.
- A bar showing percentage complete.
- The percentage as a number (e.g. '42%').
- An M-of-N counter (e.g. '42/100').
- Elapsed time since the task started.
- Estimated time remaining.
Use the result as a context manager to start and stop rendering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transient
|
bool
|
If True, the progress bar is cleared from the terminal when the context manager exits. Useful for short-lived tasks where the bar would clutter the output. Defaults to False. |
False
|
console
|
Console | None
|
The :class: |
None
|
Returns:
| Type | Description |
|---|---|
Progress
|
A configured |
Raises:
| Type | Description |
|---|---|
MissingPackageError
|
If |
Example
>>> from coola.display.rich import make_progressbar
>>> rows = list(range(10))
>>> with make_progressbar() as progress:
... task = progress.add_task("Processing rows", total=len(rows))
... for row in rows:
... print(row)
... progress.advance(task)
...
coola.display.rich.make_spinner ¶
make_spinner(
*,
transient: bool = True,
console: Console | None = None,
) -> Progress
Create a Rich spinner for tasks where the total number of items is unknown.
Builds a :class:~rich.progress.Progress instance with a column
layout suited for indeterminate tasks:
- A spinner indicating the task is active.
- Description text.
- An M-of-N counter showing items processed so far (e.g.
42/?). - Processing speed (e.g.
12.3/s). - Elapsed time since the task started.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transient
|
bool
|
If |
True
|
console
|
Console | None
|
The :class: |
None
|
Returns:
| Type | Description |
|---|---|
Progress
|
A configured :class: |
Progress
|
use as a context manager. |
Raises:
| Type | Description |
|---|---|
MissingPackageError
|
If |
Example
>>> from coola.display.rich import make_spinner
>>> items = list(range(10))
>>> with make_spinner() as progress:
... task = progress.add_task("Processing items...", total=None)
... for item in items:
... progress.advance(task)
...
coola.display.rich.print_markdown ¶
print_markdown(
msg: str,
*,
title: str | None = None,
title_align: AlignMethod = "left",
box: bool = True,
panel: bool = True,
max_length: int | None = None,
console: Console | None = None,
) -> None
Render a Markdown string to the console, optionally inside a Rich panel.
Prints msg as rendered Markdown, wrapped in a
:class:~rich.panel.Panel unless panel is False. Uses the
provided console if given, otherwise falls back to the shared
instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
msg
|
str
|
The Markdown content to render. May contain any Markdown
syntax supported by :class: |
required |
title
|
str | None
|
Optional title displayed on the panel border. Pass
|
None
|
title_align
|
AlignMethod
|
Horizontal alignment of |
'left'
|
box
|
bool
|
If |
True
|
panel
|
bool
|
If |
True
|
max_length
|
int | None
|
Maximum number of characters of |
None
|
console
|
Console | None
|
Optional :class: |
None
|
Raises:
| Type | Description |
|---|---|
MissingPackageError
|
If |
Raises:
| Type | Description |
|---|---|
MissingPackageError
|
If |
ValueError
|
If |
Example
>>> from io import StringIO
>>> from rich.console import Console
>>> from coola.display.rich import print_markdown
>>> console = Console(file=StringIO(), width=20)
>>> print_markdown("**hello**", panel=False, console=console)
>>> console.file.getvalue().strip()
'hello'
>>> print_markdown("**hello**", title="Demo")
>>> print_markdown("**hello**", title="Demo", box=False)
>>> print_markdown("**hello**", panel=False)
coola.display.rich.print_pretty ¶
print_pretty(
data: Any,
*,
title: str | None = None,
title_align: AlignMethod = "left",
box: bool = True,
panel: bool = True,
console: Console | None = None,
) -> None
Render an arbitrary object to the console in a pretty format, optionally inside a Rich panel.
Prints data using :class:~rich.pretty.Pretty, wrapped in a
:class:~rich.panel.Panel unless panel is False. Uses the
provided console if given, otherwise falls back to the shared
instance. Works with any Python object — dicts, lists, dataclasses,
Pydantic models, and so on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
The object to render. Passed directly to
:class: |
required |
title
|
str | None
|
Optional title displayed on the panel border. Pass
|
None
|
title_align
|
AlignMethod
|
Horizontal alignment of |
'left'
|
box
|
bool
|
If |
True
|
panel
|
bool
|
If |
True
|
console
|
Console | None
|
Optional :class: |
None
|
Raises:
| Type | Description |
|---|---|
MissingPackageError
|
If |
Example
>>> from io import StringIO
>>> from rich.console import Console
>>> from coola.display.rich import print_pretty
>>> console = Console(file=StringIO(), width=20)
>>> print_pretty({"key": "value"}, panel=False, console=console)
>>> console.file.getvalue().strip()
"{'key': 'value'}"
>>> print_pretty({"key": "value"}, title="Demo")
>>> print_pretty({"key": "value"}, title="Demo", box=False)
>>> print_pretty({"key": "value"}, panel=False)