1515import logging
1616import os
1717from pathlib import Path
18+ import sys
1819import warnings
1920
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+
2028import 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
2230
2331_log = logging .getLogger (__name__ )
2432
6472"directly use the seaborn API instead." )
6573
6674
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-
8475@_docstring .Substitution (
8576"\n " .join (map ("- {}" .format ,sorted (STYLE_BLACKLIST ,key = str .lower )))
8677)
@@ -99,20 +90,28 @@ def use(style):
9990 Parameters
10091 ----------
10192 style : str, dict, Path or list
102- A style specification. Valid options are:
10393
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.
116115
117116 Notes
118117 -----
@@ -129,33 +128,52 @@ def use(style):
129128
130129style_alias = {'mpl20' :'default' ,'mpl15' :'classic' }
131130
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 :
136135_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 )):
151161try :
152- rc = rc_params_from_file (style ,use_default_template = False )
153- _apply_style (rc )
162+ style = _rc_params_in_file (style )
154163except IOError as err :
155164raise 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 )
159177
160178
161179@contextlib .contextmanager
@@ -205,8 +223,7 @@ def read_style_directory(style_dir):
205223styles = dict ()
206224for path in Path (style_dir ).glob (f"*.{ STYLE_EXTENSION } " ):
207225with 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 )
210227for w in warns :
211228_log .warning ('In %s: %s' ,path ,w .message )
212229return styles