Skip to content

Commit 01c8743

Browse files
committed
Load style files from third-party packages.
1 parent 31d4f24 commit 01c8743

File tree

9 files changed

+94
-18
lines changed

9 files changed

+94
-18
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ jobs:
162162
163163
# Install dependencies from PyPI.
164164
python -m pip install --upgrade $PRE \
165-
'contourpy>=1.0.1' cycler fonttools kiwisolver numpy packaging \
166-
pillow pyparsing python-dateutil setuptools-scm \
165+
'contourpy>=1.0.1' cycler fonttools kiwisolver importlib_resources \
166+
numpy packaging pillow pyparsing python-dateutil setuptools-scm \
167167
-r requirements/testing/all.txt \
168168
${{ matrix.extra-requirements }}
169169
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
importlib_resources>=2.3.0 is now required on Python<3.10
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

doc/devel/dependencies.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ reference.
2626
* `Pillow <https://pillow.readthedocs.io/en/latest/>`_ (>= 6.2)
2727
* `pyparsing <https://pypi.org/project/pyparsing/>`_ (>= 2.3.1)
2828
* `setuptools <https://setuptools.readthedocs.io/en/latest/>`_
29+
* `pyparsing <https://pypi.org/project/pyparsing/>`_ (>= 2.3.1)
30+
* `importlib-resources <https://pypi.org/project/importlib-resources/>`_
31+
(>= 3.2.0; only required on Python < 3.10)
2932

3033

3134
.. _optional_dependencies:
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Style files can be imported from third-party packages
2+
-----------------------------------------------------
3+
4+
Third-party packages can now distribute style files that are globally available
5+
as follows. Assume that a package is importable as ``import mypackage``, with
6+
a ``mypackage/__init__.py`` module. Then a ``mypackage/presentation.mplstyle``
7+
style sheet can be used as ``plt.style.use("mypackage.presentation")``.
8+
9+
The implementation does not actually import ``mypackage``, making this process
10+
safe against possible import-time side effects. Subpackages (e.g.
11+
``dotted.package.name`` are also supported).

lib/matplotlib/style/core.py

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@
1616
import os
1717
from pathlib import Path
1818
import re
19+
import sys
1920
import warnings
2021

22+
if sys.version_info >= (3, 10):
23+
import importlib.resources as importlib_resources
24+
else:
25+
# Even though Py3.9 has importlib.resources, it doesn't properly handle
26+
# modules added in sys.path.
27+
import importlib_resources
28+
2129
import matplotlib as mpl
2230
from matplotlib import _api, _docstring, _rc_params_in_file, rcParamsDefault
2331

@@ -89,20 +97,27 @@ def use(style):
8997
Parameters
9098
----------
9199
style : str, dict, Path or list
92-
A style specification. Valid options are:
93100
94-
+------+-------------------------------------------------------------+
95-
| str | The name of a style or a path/URL to a style file. For a |
96-
| | list of available style names, see `.style.available`. |
97-
+------+-------------------------------------------------------------+
98-
| dict | Dictionary with valid key/value pairs for |
99-
| | `matplotlib.rcParams`. |
100-
+------+-------------------------------------------------------------+
101-
| Path | A path-like object which is a path to a style file. |
102-
+------+-------------------------------------------------------------+
103-
| list | A list of style specifiers (str, Path or dict) applied from |
104-
| | first to last in the list. |
105-
+------+-------------------------------------------------------------+
101+
A style specification.
102+
103+
- If a str, this can be one of the style names in `.style.available`
104+
(a builtin style or a style installed in the user library path).
105+
106+
This can also be a dotted name of the form "package.name.style_name";
107+
in that case, "package.name" should be an importable Python package
108+
name, e.g. at ``/path/to/package/name/__init__.py``; the loaded style
109+
file is ``/path/to/package/name/style_name.mplstyle``.
110+
111+
This can also be the path or URL to a style file, which gets loaded
112+
by `.rc_params_from_file`.
113+
114+
- If a dict, this is a mapping of key/value pairs for `.rcParams`.
115+
116+
- If a Path, this is the path to a style file, which gets loaded by
117+
`.rc_params_from_file`.
118+
119+
- If a list, this is a a list of style specifiers (str, Path or dict),
120+
which get applied from first to last in the list.
106121
107122
Notes
108123
-----
@@ -134,6 +149,20 @@ def use(style):
134149
if k not in STYLE_BLACKLIST}
135150
elif style in library:
136151
style = library[style]
152+
elif "." in style:
153+
pkg, _, name = style.rpartition(".")
154+
try:
155+
path = (importlib_resources.files(pkg)
156+
/ f"{name}.{STYLE_EXTENSION}")
157+
style = _rc_params_in_file(path)
158+
except (ModuleNotFoundError, IOError) as exc:
159+
# There is an ambiguity whether a dotted name refers to a
160+
# package.style_name or to a dotted file path. Currently,
161+
# we silently try the first form and then the second one;
162+
# in the future, we may consider forcing file paths to
163+
# either use Path objects or be prepended with "./" and use
164+
# the slash as marker for file paths.
165+
pass
137166
if isinstance(style, (str, Path)):
138167
try:
139168
style = _rc_params_in_file(style)

lib/matplotlib/tests/test_style.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,18 @@ def test_deprecated_seaborn_styles():
190190

191191
def test_up_to_date_blacklist():
192192
assert mpl.style.core.STYLE_BLACKLIST <= {*mpl.rcsetup._validators}
193+
194+
195+
def test_style_from_module(tmp_path, monkeypatch):
196+
monkeypatch.syspath_prepend(tmp_path)
197+
monkeypatch.chdir(tmp_path)
198+
pkg_path = tmp_path / "mpl_test_style_pkg"
199+
pkg_path.mkdir()
200+
(pkg_path / "test_style.mplstyle").write_text(
201+
"lines.linewidth: 42", encoding="utf-8")
202+
pkg_path.with_suffix(".mplstyle").write_text(
203+
"lines.linewidth: 84", encoding="utf-8")
204+
mpl.style.use("mpl_test_style_pkg.test_style")
205+
assert mpl.rcParams["lines.linewidth"] == 42
206+
mpl.style.use("mpl_test_style_pkg.mplstyle")
207+
assert mpl.rcParams["lines.linewidth"] == 84

requirements/testing/minver.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
contourpy==1.0.1
44
cycler==0.10
55
kiwisolver==1.0.1
6+
importlib-resources==3.2.0
67
numpy==1.19.0
78
packaging==20.0
89
pillow==6.2.1

setup.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,11 @@ def make_release_tree(self, base_dir, files):
327327
os.environ.get("CIBUILDWHEEL", "0") != "1"
328328
) else []
329329
),
330+
extras_require={
331+
':python_version<"3.10"': [
332+
"importlib-resources>=3.2.0",
333+
],
334+
},
330335
use_scm_version={
331336
"version_scheme": "release-branch-semver",
332337
"local_scheme": "node-and-date",

tutorials/introductory/customizing.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
1010
There are three ways to customize Matplotlib:
1111
12-
1. :ref:`Setting rcParams at runtime<customizing-with-dynamic-rc-settings>`.
13-
2. :ref:`Using style sheets<customizing-with-style-sheets>`.
14-
3. :ref:`Changing your matplotlibrc file<customizing-with-matplotlibrc-files>`.
12+
1. :ref:`Setting rcParams at runtime<customizing-with-dynamic-rc-settings>`.
13+
2. :ref:`Using style sheets<customizing-with-style-sheets>`.
14+
3. :ref:`Changing your matplotlibrc file<customizing-with-matplotlibrc-files>`.
1515
1616
Setting rcParams at runtime takes precedence over style sheets, style
1717
sheets take precedence over :file:`matplotlibrc` files.
@@ -137,6 +137,16 @@ def plotting_function():
137137
# >>> import matplotlib.pyplot as plt
138138
# >>> plt.style.use('./images/presentation.mplstyle')
139139
#
140+
#
141+
# Distributing styles
142+
# -------------------
143+
#
144+
# You can include style sheets into standard importable Python packages (which
145+
# can be e.g. distributed on PyPI). If your package is importable as
146+
# ``import my.package``, with a ``my/package/__init__.py`` module, and you add
147+
# a ``my/package/presentation.mplstyle`` style sheet, then it can be used as
148+
# ``plt.style.use("my.package.presentation")``.
149+
#
140150
# Alternatively, you can make your style known to Matplotlib by placing
141151
# your ``<style-name>.mplstyle`` file into ``mpl_configdir/stylelib``. You
142152
# can then load your custom style sheet with a call to

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy