15
15
import logging
16
16
import os
17
17
from pathlib import Path
18
+ import sys
18
19
import warnings
19
20
21
+ if sys .version_info >= (3 , 10 ):
22
+ import importlib .resources as importlib_resources
23
+ else :
24
+ # Even though Py3.9 has importlib.resources, it doesn't properly handle
25
+ # modules added in sys.path.
26
+ import importlib_resources
27
+
20
28
import matplotlib as mpl
21
- from matplotlib import _api , _docstring , rc_params_from_file , rcParamsDefault
29
+ from matplotlib import _api , _docstring , _rc_params_in_file , rcParamsDefault
22
30
23
31
_log = logging .getLogger (__name__ )
24
32
64
72
"directly use the seaborn API instead." )
65
73
66
74
67
- def _remove_blacklisted_style_params (d , warn = True ):
68
- o = {}
69
- for key in d : # prevent triggering RcParams.__getitem__('backend')
70
- if key in STYLE_BLACKLIST :
71
- if warn :
72
- _api .warn_external (
73
- f"Style includes a parameter, { key !r} , that is not "
74
- "related to style. Ignoring this parameter." )
75
- else :
76
- o [key ] = d [key ]
77
- return o
78
-
79
-
80
- def _apply_style (d , warn = True ):
81
- mpl .rcParams .update (_remove_blacklisted_style_params (d , warn = warn ))
82
-
83
-
84
75
@_docstring .Substitution (
85
76
"\n " .join (map ("- {}" .format , sorted (STYLE_BLACKLIST , key = str .lower )))
86
77
)
@@ -99,20 +90,28 @@ def use(style):
99
90
Parameters
100
91
----------
101
92
style : str, dict, Path or list
102
- A style specification. Valid options are:
103
93
104
- +------+-------------------------------------------------------------+
105
- | str | The name of a style or a path/URL to a style file. For a |
106
- | | list of available style names, see `.style.available`. |
107
- +------+-------------------------------------------------------------+
108
- | dict | Dictionary with valid key/value pairs for |
109
- | | `matplotlib.rcParams`. |
110
- +------+-------------------------------------------------------------+
111
- | Path | A path-like object which is a path to a style file. |
112
- +------+-------------------------------------------------------------+
113
- | list | A list of style specifiers (str, Path or dict) applied from |
114
- | | first to last in the list. |
115
- +------+-------------------------------------------------------------+
94
+ A style specification.
95
+
96
+ - If a str, this can be one of the style names in `.style.available`
97
+ (a builtin style or a style installed in the user library path).
98
+
99
+ This can also be a dotted name of the form "package.style_name"; in
100
+ that case, "package" should be an importable Python package name,
101
+ e.g. at ``/path/to/package/__init__.py``; the loaded style file is
102
+ ``/path/to/package/style_name.mplstyle``. (Style files in
103
+ subpackages are likewise supported.)
104
+
105
+ This can also be the path or URL to a style file, which gets loaded
106
+ by `.rc_params_from_file`.
107
+
108
+ - If a dict, this is a mapping of key/value pairs for `.rcParams`.
109
+
110
+ - If a Path, this is the path to a style file, which gets loaded by
111
+ `.rc_params_from_file`.
112
+
113
+ - If a list, this is a list of style specifiers (str, Path or dict),
114
+ which get applied from first to last in the list.
116
115
117
116
Notes
118
117
-----
@@ -129,33 +128,52 @@ def use(style):
129
128
130
129
style_alias = {'mpl20' : 'default' , 'mpl15' : 'classic' }
131
130
132
- def fix_style ( s ) :
133
- if isinstance (s , str ):
134
- s = style_alias .get (s , s )
135
- if s in _DEPRECATED_SEABORN_STYLES :
131
+ for style in styles :
132
+ if isinstance (style , str ):
133
+ style = style_alias .get (style , style )
134
+ if style in _DEPRECATED_SEABORN_STYLES :
136
135
_api .warn_deprecated ("3.6" , message = _DEPRECATED_SEABORN_MSG )
137
- s = _DEPRECATED_SEABORN_STYLES [s ]
138
- return s
139
-
140
- for style in map (fix_style , styles ):
141
- if not isinstance (style , (str , Path )):
142
- _apply_style (style )
143
- elif style == 'default' :
144
- # Deprecation warnings were already handled when creating
145
- # rcParamsDefault, no need to reemit them here.
146
- with _api .suppress_matplotlib_deprecation_warning ():
147
- _apply_style (rcParamsDefault , warn = False )
148
- elif style in library :
149
- _apply_style (library [style ])
150
- else :
136
+ style = _DEPRECATED_SEABORN_STYLES [style ]
137
+ if style == "default" :
138
+ # Deprecation warnings were already handled when creating
139
+ # rcParamsDefault, no need to reemit them here.
140
+ with _api .suppress_matplotlib_deprecation_warning ():
141
+ # don't trigger RcParams.__getitem__('backend')
142
+ style = {k : rcParamsDefault [k ] for k in rcParamsDefault
143
+ if k not in STYLE_BLACKLIST }
144
+ elif style in library :
145
+ style = library [style ]
146
+ elif "." in style :
147
+ pkg , _ , name = style .rpartition ("." )
148
+ try :
149
+ path = (importlib_resources .files (pkg )
150
+ / f"{ name } .{ STYLE_EXTENSION } " )
151
+ style = _rc_params_in_file (path )
152
+ except (ModuleNotFoundError , IOError ) as exc :
153
+ # There is an ambiguity whether a dotted name refers to a
154
+ # package.style_name or to a dotted file path. Currently,
155
+ # we silently try the first form and then the second one;
156
+ # in the future, we may consider forcing file paths to
157
+ # either use Path objects or be prepended with "./" and use
158
+ # the slash as marker for file paths.
159
+ pass
160
+ if isinstance (style , (str , Path )):
151
161
try :
152
- rc = rc_params_from_file (style , use_default_template = False )
153
- _apply_style (rc )
162
+ style = _rc_params_in_file (style )
154
163
except IOError as err :
155
164
raise IOError (
156
- "{!r} not found in the style library and input is not a "
157
- "valid URL or path; see `style.available` for list of "
158
- "available styles" .format (style )) from err
165
+ f"{ style !r} is not a valid package style, path of style "
166
+ f"file, URL of style file, or library style name (library "
167
+ f"styles are listed in `style.available`)" ) from err
168
+ filtered = {}
169
+ for k in style : # don't trigger RcParams.__getitem__('backend')
170
+ if k in STYLE_BLACKLIST :
171
+ _api .warn_external (
172
+ f"Style includes a parameter, { k !r} , that is not "
173
+ f"related to style. Ignoring this parameter." )
174
+ else :
175
+ filtered [k ] = style [k ]
176
+ mpl .rcParams .update (filtered )
159
177
160
178
161
179
@contextlib .contextmanager
@@ -205,8 +223,7 @@ def read_style_directory(style_dir):
205
223
styles = dict ()
206
224
for path in Path (style_dir ).glob (f"*.{ STYLE_EXTENSION } " ):
207
225
with warnings .catch_warnings (record = True ) as warns :
208
- styles [path .stem ] = rc_params_from_file (
209
- path , use_default_template = False )
226
+ styles [path .stem ] = _rc_params_in_file (path )
210
227
for w in warns :
211
228
_log .warning ('In %s: %s' , path , w .message )
212
229
return styles
0 commit comments