Global Defaults — dm.config

dm.config is the process-wide singleton that holds dartwork-mpl’s behaviour toggles. Set it once near the top of your program (or in a notebook’s first cell) and every subsequent call across dm.layout, dm.io, and friends honours the new default — no need to thread the keyword through every call site.

Per-call overrides always win. Passing simple_layout(fig, adopt_orphan_tick_font=True) overrides whatever you set on dm.config. The singleton supplies the fallback only.

What’s in there

dm.config is a @dataclass with named fields. Today there are two; more land as audit findings ship.

Field

Default

Affects

adopt_orphan_tick_font

False

simple_layout, save_formats, save_and_show — when enabled and an axis has tick labels but no axis label, dartwork copies the axis label font (size + weight + family) onto the ticks. Set True to opt in; otherwise tick fonts stay untouched.

warn_on_orphan_tick_adoption

False

Same three call sites. When True, emits a one-time UserWarning every time the adoption mutates a figure. Useful when debugging surprise tick changes, or when running under matplotlib’s constrained_layout where the font swap can trigger a re-layout.

The full surface is in config.py — that’s the SSOT, this page is the cookbook.

Patterns

1. Opt in project-wide

Set once near the top of your entry script or notebook. Every simple_layout / save_formats / save_and_show call afterwards picks it up.

import matplotlib.pyplot as plt
import dartwork_mpl as dm

# This project prefers orphan ticks to use the axis-label font.
dm.config.adopt_orphan_tick_font = True

dm.style.use("scientific")

fig, ax = plt.subplots(figsize=dm.figsize("13cm", "standard"))
ax.plot([1, 2, 3], [1, 4, 9])

dm.simple_layout(fig)          # tick fonts adopted (config wins)
dm.save_formats(fig, "out", formats=("png", "svg"))

2. Scope the change to a with block

dm.config.override(...) restores the previous values on exit, even on exception. Use this when most of your code wants the default but one specific export needs the opposite.

with dm.config.override(adopt_orphan_tick_font=True):
    dm.simple_layout(fig)                    # adoption ON here
    dm.save_formats(fig, "press_release", formats=("pdf",))

dm.simple_layout(fig)                        # back to OFF

The block is exception-safe — raising inside still restores every overridden field.

3. Per-call override beats config

Pass the keyword explicitly when you want to bypass dm.config for a single call. Anything you pass (True or False) wins; the only value that defers to dm.config is None (the call-site default).

dm.config.adopt_orphan_tick_font = True      # project default

# Just this figure: skip adoption.
dm.simple_layout(fig, adopt_orphan_tick_font=False)

4. Debug surprise tick changes

When a tick font silently changes between two draws (a common constrained-layout / re-layout hazard), turn the warning on. Every adoption emits a UserWarning that includes the axis it touched, so you can isolate the offender.

import warnings

dm.config.warn_on_orphan_tick_adoption = True

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    dm.simple_layout(fig)

for w in caught:
    print(w.message)
# UserWarning: orphan-tick font adoption mutated 2 axes ...

Pair with dm.config.adopt_orphan_tick_font = False and the warning goes silent — confirming you’ve fully opted out.

5. Typos fail loudly

override(...) raises AttributeError for any field name that isn’t declared on Config. A typo can’t silently shadow the singleton.

try:
    with dm.config.override(adopt_orphn_tick_font=False):   # typo
        ...
except AttributeError as exc:
    print(exc)
# dm.config has no attribute(s): ['adopt_orphn_tick_font']. Known fields: ...

Use this to your advantage: when you suspect a stale doc, just try the field name — if it fails, the field’s gone or never existed.

Mental model

        flowchart TD
    A["<b>① per-call keyword</b><br/><code>simple_layout(fig, adopt_orphan_tick_font=False)</code><br/><i>highest priority — always wins</i>"]
    B["<b>② dm.config field</b><br/><code>dm.config.adopt_orphan_tick_font = False</code><br/><i>project-wide default</i>"]
    C["<b>③ hard-coded Config default</b><br/><code>False</code><br/><i>library fallback</i>"]
    A -->|"keyword is None → fall through"| B
    B -->|"field unset → fall through"| C

    style A fill:#fef3c7,stroke:#f0c000,stroke-width:1.5px,color:#1c2024
    style B fill:#ecfeff,stroke:#7cd4e0,stroke-width:1px,color:#1c2024
    style C fill:#f3e8ff,stroke:#c4a0e8,stroke-width:1px,color:#1c2024
    

The chain is deterministic and read-only at the lower levels — the per-call keyword never mutates dm.config, and dm.config never mutates the dataclass defaults.

When to add a new field

For library contributors — most users can skip this section; it’s about proposing a new dm.config field, not using the existing ones.

Don’t reach for dm.config for everything. Use it when:

  • The toggle is a bool / enum default the user might want to flip for a whole project, not a per-call decision.

  • There’s no preset-level home for it (preset attributes are for style, dm.config is for behaviour).

  • The current default is silent — i.e., users today have no knob, and one would help.

Skip it when:

  • The setting belongs to a preset (dm.style.use(...)).

  • The setting is truly call-site (file paths, individual figure options).

The roadmap of candidate fields is tracked at issue #311 (B19 — config audit).

See also

  • Layout and Typography — where adopt_orphan_tick_font actually fires.

  • Save and Validationsave_formats / save_and_show read the same field.

  • dm.dpi(n) — the DPI ladder (preset’s base savefig.dpi + n × 50) that pairs naturally with these defaults. With the presets now shipping 300 as base, dm.dpi() returns 300.0, dm.dpi(1) returns 350.0, etc.