Generic matplotlib brand/corporate-design framework — patcher, pyplot hooks, style registry, and pandas hook for building company-specific plot packages
Project description
mpl-brandpacker
Package your brand design into a matplotlib.pyplot drop-in replacement. Define titles, headers styling, colors, and axes styling as Python classes — mpl-brandpacker handles the patching so your_brand.pyplot.subplots() returns fully branded figures and axes.
Install
pip install mpl-brandpacker
Quick start
1. Define your brand
# my_brand/colors.py
import mpl_brandpacker as mbp
class Colors(mbp.ColorsBase):
primary = "#1a5276"
accent = "#e67e22"
dark = "#2c3e50"
muted = "slategray" # named colors work too
cycle_0 = "C0" # matplotlib cycle colors
highlight = "coral" # CSS named colors
Colors.plot() # → swatch grid for documentation
ColorsBase accepts any matplotlib color string (#RRGGBB, #RRGGBBAA, #RGB, named colors, C0–C9, tab:blue, etc.) and normalizes to #rrggbb at definition time.
# my_brand/sizes.py
from mpl_brandpacker.sizes import MM_TO_INCH
class Sizes(mbp.FigsizesBase):
half = (88 * MM_TO_INCH, 76 * MM_TO_INCH)
full = (181 * MM_TO_INCH, 76 * MM_TO_INCH)
class FontSizes(mbp.SizesBase):
title = 10
body = 8
footer = 6.5
_scalers = {"presentation": 2.0}
SizesBase.scaled() is thread-safe — concurrent threads and async tasks each get their own scaled values.
# my_brand/figure.py
from mpl_brandpacker import BrandFigure, brand_method
class MyFigure(BrandFigure):
@brand_method
def set_title(self, title, **kw):
self.mpl.suptitle(title, fontsize=10, weight="bold", x=0.02, ha="left", **kw)
@brand_method
def set_sources(self, sources, **kw):
self.text(0.02, 0.02, f"Source: {sources}", fontsize=6.5,
color="#888", transform=self.transFigure, **kw)
@brand_method(overwrite="savefig")
def _branded_save(self, *args, **kw):
"""Override savefig with branded defaults.
Pylance still shows the original Figure.savefig docstring
because the implementation is underscore-prefixed.
"""
kw.setdefault("dpi", 300)
kw.setdefault("bbox_inches", "tight")
self.mpl.savefig(*args, **kw)
Use @brand_method(overwrite="name") to override a built-in method while keeping IDE autocompletion on the original.
# my_brand/axes.py
from mpl_brandpacker import BrandAxes, brand_method
class MyAxes(BrandAxes):
@brand_method
def set_xlabel(self, label, **kw):
self.mpl.set_xlabel(label, fontsize=8, **kw)
@brand_method
def set_ylabel(self, label, **kw):
self.mpl.set_ylabel(label, fontsize=8, rotation="horizontal", **kw)
def set_style(ax, **kw):
ax.grid(True, alpha=0.2)
for spine in ("top", "right"):
ax.spines[spine].set_visible(False)
2. Configure
# my_brand/__init__.py
import mpl_brandpacker as mbp
from pathlib import Path
mbp.configure(
figure_cls=MyFigure,
axes_cls=MyAxes,
style_fn=set_style,
stylesheet=Path(__file__).parent, # dir with .mplstyle + data/fonts/
pandas=True, # also hook df.plot()
)
3. Re-export pyplot
# my_brand/pyplot.py
from mpl_brandpacker.pyplot import * # noqa
from mpl_brandpacker.pyplot import gcf
# pyplot-level shortcuts for brand methods (like plt.title in matplotlib)
def title(title, **kw): gcf().set_title(title, **kw)
def subtitle(sub, **kw): gcf().set_subtitle(sub, **kw)
def sources(src, **kw): gcf().set_sources(src, **kw)
def footnote(note, **kw): gcf().set_footnote(note, **kw)
For IDE autocompletion of brand methods on fig and ax, add a pyplot.pyi stub file — see the template for a complete example.
4. Use it
import my_brand.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [4, 5, 6])
ax.set_xlabel("Quarter")
plt.title("Revenue")
plt.sources("Bloomberg")
plt.show()
Everything is branded. Original matplotlib methods are accessible via .mpl:
fig.mpl.legend() # original Figure.legend
ax.mpl.set_xlabel() # original Axes.set_xlabel
5. Reset (notebooks)
When iterating on your brand in a notebook, use reset() to start fresh:
import mpl_brandpacker as mbp
mbp.reset() # clears all hooks, reverts pandas
mbp.configure(figure_cls=..., ...) # re-configure
What it provides
| Export | Purpose |
|---|---|
configure() |
Wire everything up — one call |
reset() |
Clear all hooks (for notebook iteration) |
brand_method |
Decorator to auto-register methods for patching |
BrandFigure |
Base class for figure methods (subclass of Figure) |
BrandAxes |
Base class for axes methods (subclass of Axes) |
ColorsBase |
Color enum — accepts any matplotlib color string, normalizes to hex |
FigsizesBase |
Validated (w,h) size enum with .plot() |
SizesBase |
Font sizes with thread-safe context-managed scaling |
PrintableEnum |
Generic enum base |
Submodules (advanced)
| Module | Purpose |
|---|---|
mpl_brandpacker.pyplot |
Branded drop-in for matplotlib.pyplot |
mpl_brandpacker.pandas |
use_for_pandas() for df.plot() |
mpl_brandpacker.patcher |
brand_method, patch_method(), MethodProxy |
mpl_brandpacker.style |
register_stylesheet() |
mpl_brandpacker.sizes |
MM_TO_INCH, POINTS_TO_INCH |
mpl_brandpacker.utils |
get_text_bbox(), separate_kwargs() |
How it works
configure() builds two functions — make_fig(fig) and make_ax(ax) — that patch matplotlib objects using Python's descriptor protocol. When you import my_brand.pyplot as plt, three pyplot entry points are intercepted:
figure()→ patches the new figure + wraps its axes creation methodsgcf()→ patches the current figuregca()→ patches the current axes
All other pyplot functions (subplots, show, savefig, etc.) work unchanged because they internally call figure() or gcf().
@brand_method decorator
@brand_method # patches fig.set_title
def set_title(self, title): ...
@brand_method(overwrite="savefig") # patches fig.savefig with this method
def _branded_save(self, *a, **kw): # underscore name → invisible to Pylance
self.mpl.savefig(*a, dpi=300) # .mpl accesses the original
The overwrite parameter lets you override built-in matplotlib methods while keeping Pylance's autocompletion on the original signature.
Scaffold a new brand
python -m mpl_brandpacker.create_brand acme_corp --author "Jane Doe"
Generates a complete brand package from the built-in template with colors, sizes, figure, axes, header/footer layout, and a pyplot.pyi stub for IDE support.
Template
See template/ for a complete working example.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mpl_brandpacker-0.0.2.tar.gz.
File metadata
- Download URL: mpl_brandpacker-0.0.2.tar.gz
- Upload date:
- Size: 177.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96efd7993272e287b246348c245c40c3604a0dafb6ad31cf4aa01dd4a6dce39f
|
|
| MD5 |
f094238af188ab3ae14af7ec077aea30
|
|
| BLAKE2b-256 |
942c33f578cb9b9e299719ae80141596ee6c7d5b241a1d12d34d0d1e236266d4
|
Provenance
The following attestation bundles were made for mpl_brandpacker-0.0.2.tar.gz:
Publisher:
publish.yml on saemeon/mpl-brandpacker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mpl_brandpacker-0.0.2.tar.gz -
Subject digest:
96efd7993272e287b246348c245c40c3604a0dafb6ad31cf4aa01dd4a6dce39f - Sigstore transparency entry: 1236908050
- Sigstore integration time:
-
Permalink:
saemeon/mpl-brandpacker@6c80bb6b25a80e046d8284b2b6a28ca050048f30 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/saemeon
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6c80bb6b25a80e046d8284b2b6a28ca050048f30 -
Trigger Event:
release
-
Statement type:
File details
Details for the file mpl_brandpacker-0.0.2-py3-none-any.whl.
File metadata
- Download URL: mpl_brandpacker-0.0.2-py3-none-any.whl
- Upload date:
- Size: 37.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3430b7c11ec5bcde04519f8278922cde9781eec395ae87e75eebccd54c3bc4ea
|
|
| MD5 |
72aeb809af289db71e70a1bd8a28d586
|
|
| BLAKE2b-256 |
7cd4df412b06b12db143b7a852c0f7dc4928b48bce120d9c9cf8e7d84a3c7e94
|
Provenance
The following attestation bundles were made for mpl_brandpacker-0.0.2-py3-none-any.whl:
Publisher:
publish.yml on saemeon/mpl-brandpacker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mpl_brandpacker-0.0.2-py3-none-any.whl -
Subject digest:
3430b7c11ec5bcde04519f8278922cde9781eec395ae87e75eebccd54c3bc4ea - Sigstore transparency entry: 1236908078
- Sigstore integration time:
-
Permalink:
saemeon/mpl-brandpacker@6c80bb6b25a80e046d8284b2b6a28ca050048f30 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/saemeon
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6c80bb6b25a80e046d8284b2b6a28ca050048f30 -
Trigger Event:
release
-
Statement type: