Skip to content

Commit

Permalink
marimo.app_meta.theme and custom themers to auto switch plotting them…
Browse files Browse the repository at this point in the history
…es based on the display theme (#2126)

* some changes for the Chinese README

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* make the gutter bigger when the window is super wide

* revert changes for fixing #2035

* Update README_Chinese.md highlights

* first attemptation

* remove "system" option from display theme

* Add marimo.app_meta

* Add themers to auto switch plotting themes based on the display theme

* fix type errors

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* revert changes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* revert fe changes

* revert fe changes

* apply_theme for formatters

* update app_meta

* some small errors

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* set default value for theme

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update api.yaml

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Myles Scolnick <[email protected]>
  • Loading branch information
3 people authored Aug 29, 2024
1 parent 8560fee commit da7b2a7
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 5 deletions.
9 changes: 8 additions & 1 deletion marimo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"MarimoIslandGenerator",
"accordion",
"carousel",
"app_meta",
"as_html",
"audio",
"callout",
Expand Down Expand Up @@ -109,7 +110,13 @@
)
from marimo._runtime.context.utils import running_in_notebook
from marimo._runtime.control_flow import MarimoStopError, stop
from marimo._runtime.runtime import cli_args, defs, query_params, refs
from marimo._runtime.runtime import (
app_meta,
cli_args,
defs,
query_params,
refs,
)
from marimo._runtime.state import state
from marimo._server.asgi import create_asgi_app
from marimo._sql.sql import sql
3 changes: 2 additions & 1 deletion marimo/_config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class RuntimeConfig(TypedDict):
# TODO(akshayka): remove normal, migrate to compact
# normal == compact
WidthType = Literal["normal", "compact", "medium", "full"]
Theme = Literal["light", "dark", "system"]


@mddoc
Expand All @@ -116,7 +117,7 @@ class DisplayConfig(TypedDict):
- `dataframes`: `"rich"` or `"plain"`
"""

theme: Literal["light", "dark", "system"]
theme: Theme
code_editor_font_size: int
cell_output: Literal["above", "below"]
default_width: WidthType
Expand Down
6 changes: 6 additions & 0 deletions marimo/_output/formatters/altair_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import html

from marimo._config.config import Theme
from marimo._messaging.mimetypes import KnownMimeType
from marimo._output.builder import h
from marimo._output.formatters.formatter_factory import FormatterFactory
Expand Down Expand Up @@ -48,3 +49,8 @@ def _show_chart(chart: altair.Chart) -> tuple[KnownMimeType, str]:
)
),
)

def apply_theme(self, theme: Theme) -> None:
import altair as alt # type: ignore

alt.themes.enable("dark" if theme == "dark" else "default") # type: ignore
6 changes: 6 additions & 0 deletions marimo/_output/formatters/bokeh_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from typing import Optional

from marimo._config.config import Theme
from marimo._messaging.mimetypes import KnownMimeType
from marimo._output.builder import h
from marimo._output.formatters.formatter_factory import FormatterFactory
Expand Down Expand Up @@ -67,3 +68,8 @@ def _show_plot(
)
),
)

def apply_theme(self, theme: Theme) -> None:
from bokeh.io import curdoc # type: ignore

curdoc().theme = "dark_minimal" if theme == "dark" else None # type: ignore
13 changes: 13 additions & 0 deletions marimo/_output/formatters/formatter_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import abc
from typing import Callable, Optional

from marimo._config.config import Theme


# Abstract base class for formatters that are installed at runtime.
class FormatterFactory(abc.ABC):
Expand All @@ -29,3 +31,14 @@ def register(self) -> Callable[[], None] | None:
patches.
"""
raise NotImplementedError

def apply_theme(self, theme: Theme) -> None:
"""
Apply the theme (light/dark) to third party libraries.
If the theme is set to "system", then we fallback to "light".
Args:
theme: The theme to apply.
"""
del theme
return
6 changes: 5 additions & 1 deletion marimo/_output/formatters/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys
from typing import Any, Callable, Sequence

from marimo._config.config import Theme
from marimo._output.formatters.altair_formatters import AltairFormatter
from marimo._output.formatters.anywidget_formatters import AnyWidgetFormatter
from marimo._output.formatters.bokeh_formatters import BokehFormatter
Expand Down Expand Up @@ -55,7 +56,7 @@
]


def register_formatters() -> None:
def register_formatters(theme: Theme = "light") -> None:
"""Register formatters with marimo.
marimo comes packaged with rich formatters for a number of third-party
Expand All @@ -81,6 +82,7 @@ def register_formatters() -> None:
for package, factory in THIRD_PARTY_FACTORIES.items():
if package in sys.modules:
factory.register()
factory.apply_theme(theme)
pre_registered.add(package)

third_party_factories = {
Expand Down Expand Up @@ -147,6 +149,7 @@ def exec_module(
) -> Any:
loader_return_value = original_exec_module(module)
factory.register()
factory.apply_theme(theme)
return loader_return_value

spec.loader.exec_module = exec_module
Expand All @@ -161,3 +164,4 @@ def exec_module(
# package import. So we can register them at program start-up.
for factory in NATIVE_FACTORIES:
factory.register()
factory.apply_theme(theme)
11 changes: 11 additions & 0 deletions marimo/_output/formatters/holoviews_formatters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2024 Marimo. All rights reserved.
from __future__ import annotations

from marimo._config.config import Theme
from marimo._dependencies.dependencies import DependencyManager
from marimo._messaging.mimetypes import KnownMimeType
from marimo._output.formatters.formatter_factory import FormatterFactory
Expand Down Expand Up @@ -53,3 +54,13 @@ def _show_chart(
html = as_html(backend_output)

return ("text/html", html.text)

def apply_theme(self, theme: Theme) -> None:
import holoviews as hv # type: ignore

hv.renderer("bokeh").theme = (
"dark_minimal" if theme == "dark" else None
)
hv.renderer("plotly").theme = (
"plotly_dark" if theme == "dark" else "plotly"
)
8 changes: 8 additions & 0 deletions marimo/_output/formatters/matplotlib_formatters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2024 Marimo. All rights reserved.
from __future__ import annotations

from marimo._config.config import Theme
from marimo._messaging.mimetypes import KnownMimeType
from marimo._output.formatters.formatter_factory import FormatterFactory

Expand Down Expand Up @@ -55,3 +56,10 @@ def _show_bar_container(bc: BarContainer) -> tuple[KnownMimeType, str]:
return mime_data_artist(bc.patches[0].figure) # type: ignore
else:
return ("text/plain", str(bc))

def apply_theme(self, theme: Theme) -> None:
import matplotlib # type: ignore

matplotlib.style.use(
"dark_background" if theme == "dark" else "default"
)
6 changes: 6 additions & 0 deletions marimo/_output/formatters/plotly_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import json
from typing import Any

from marimo._config.config import Theme
from marimo._messaging.mimetypes import KnownMimeType
from marimo._output.formatters.formatter_factory import FormatterFactory
from marimo._output.hypertext import Html
Expand Down Expand Up @@ -51,3 +52,8 @@ def render_plotly_dict(json: dict[Any, Any]) -> Html:
args={"figure": json, "config": resolved_config},
)
)

def apply_theme(self, theme: Theme) -> None:
import plotly.io as pio # type: ignore

pio.templates.default = "plotly_dark" if theme == "dark" else "plotly"
23 changes: 23 additions & 0 deletions marimo/_runtime/app_meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2024 Marimo. All rights reserved.
from marimo._config.utils import load_config


class AppMeta:
"""
Metadata about the app.
This is used to store metadata about the app
that is not part of the app's code or state.
"""

def __init__(self) -> None:
self.user_config = load_config()

@property
def theme(self) -> str:
"""The display theme of the app."""
theme = self.user_config["display"]["theme"] or "light"
if theme == "system":
# TODO(mscolnick): have frontend tell the backend the system theme
return "light"
return theme
20 changes: 19 additions & 1 deletion marimo/_runtime/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
from marimo._plugins.core.web_component import JSONType
from marimo._plugins.ui._core.ui_element import MarimoConvertValueException
from marimo._runtime import dataflow, handlers, marimo_pdb, patches
from marimo._runtime.app_meta import AppMeta
from marimo._runtime.complete import complete, completion_worker
from marimo._runtime.context import (
ContextNotInitializedError,
Expand Down Expand Up @@ -232,6 +233,23 @@ def query_params() -> QueryParams:
return get_context().query_params


@mddoc
def app_meta() -> AppMeta:
"""Get the metadata of a marimo app.
**Examples**:
```python3
theme = mo.app_meta().theme
```
**Returns**:
- An `AppMeta` object containing the app's metadata.
"""
return AppMeta()


@mddoc
def cli_args() -> CLIArgs:
"""Get the command line arguments of a marimo notebook.
Expand Down Expand Up @@ -1891,7 +1909,7 @@ def launch_kernel(

# kernels are processes in edit mode, and each process needs to
# install the formatter import hooks
register_formatters()
register_formatters(theme=user_config["display"]["theme"])

signal.signal(
signal.SIGINT, handlers.construct_interrupt_handler(kernel)
Expand Down
4 changes: 3 additions & 1 deletion marimo/_server/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,9 @@ def launch_kernel_with_cleanup(*args: Any) -> None:
# install formatter import hooks, which will be shared by all
# threads (in edit mode, the single kernel process installs
# formatters ...)
register_formatters()
register_formatters(
theme=self.user_config_manager.config["display"]["theme"]
)

# Make threads daemons so killing the server immediately brings
# down all client sessions
Expand Down
Loading

0 comments on commit da7b2a7

Please sign in to comment.