1
1
from __future__ import annotations
2
2
3
3
import importlib
4
+ import types
4
5
from typing import (
5
6
TYPE_CHECKING ,
6
7
Sequence ,
7
8
)
8
9
10
+ import pkg_resources
11
+
9
12
from pandas ._config import get_option
10
13
11
14
from pandas ._typing import IndexLabel
@@ -865,7 +868,7 @@ def _get_call_args(backend_name, data, args, kwargs):
865
868
if args and isinstance (data , ABCSeries ):
866
869
positional_args = str (args )[1 :- 1 ]
867
870
keyword_args = ", " .join (
868
- f"{ name } ={ repr (value )} " for (name , default ), value in zip (arg_def , args )
871
+ f"{ name } ={ repr (value )} " for (name , _ ), value in zip (arg_def , args )
869
872
)
870
873
msg = (
871
874
"`Series.plot()` should not be called with positional "
@@ -876,7 +879,7 @@ def _get_call_args(backend_name, data, args, kwargs):
876
879
)
877
880
raise TypeError (msg )
878
881
879
- pos_args = {name : value for value , (name , _ ) in zip (args , arg_def )}
882
+ pos_args = {name : value for (name , _ ), value in zip (arg_def , args )}
880
883
if backend_name == "pandas.plotting._matplotlib" :
881
884
kwargs = dict (arg_def , ** pos_args , ** kwargs )
882
885
else :
@@ -1724,91 +1727,90 @@ def hexbin(self, x, y, C=None, reduce_C_function=None, gridsize=None, **kwargs):
1724
1727
return self (kind = "hexbin" , x = x , y = y , C = C , ** kwargs )
1725
1728
1726
1729
1727
- _backends = {}
1730
+ _backends : dict [ str , types . ModuleType ] = {}
1728
1731
1729
1732
1730
- def _find_backend (backend : str ):
1733
+ def _load_backend (backend : str ) -> types . ModuleType :
1731
1734
"""
1732
- Find a pandas plotting backend>
1735
+ Load a pandas plotting backend.
1733
1736
1734
1737
Parameters
1735
1738
----------
1736
1739
backend : str
1737
1740
The identifier for the backend. Either an entrypoint item registered
1738
- with pkg_resources, or a module name.
1739
-
1740
- Notes
1741
- -----
1742
- Modifies _backends with imported backends as a side effect.
1741
+ with pkg_resources, "matplotlib", or a module name.
1743
1742
1744
1743
Returns
1745
1744
-------
1746
1745
types.ModuleType
1747
1746
The imported backend.
1748
1747
"""
1749
- import pkg_resources # Delay import for performance.
1748
+ if backend == "matplotlib" :
1749
+ # Because matplotlib is an optional dependency and first-party backend,
1750
+ # we need to attempt an import here to raise an ImportError if needed.
1751
+ try :
1752
+ module = importlib .import_module ("pandas.plotting._matplotlib" )
1753
+ except ImportError :
1754
+ raise ImportError (
1755
+ "matplotlib is required for plotting when the "
1756
+ 'default backend "matplotlib" is selected.'
1757
+ ) from None
1758
+ return module
1759
+
1760
+ found_backend = False
1750
1761
1751
1762
for entry_point in pkg_resources .iter_entry_points ("pandas_plotting_backends" ):
1752
- if entry_point .name == "matplotlib" :
1753
- # matplotlib is an optional dependency. When
1754
- # missing, this would raise.
1755
- continue
1756
- _backends [entry_point .name ] = entry_point .load ()
1763
+ found_backend = entry_point .name == backend
1764
+ if found_backend :
1765
+ module = entry_point .load ()
1766
+ break
1757
1767
1758
- try :
1759
- return _backends [backend ]
1760
- except KeyError :
1768
+ if not found_backend :
1761
1769
# Fall back to unregistered, module name approach.
1762
1770
try :
1763
1771
module = importlib .import_module (backend )
1772
+ found_backend = True
1764
1773
except ImportError :
1765
1774
# We re-raise later on.
1766
1775
pass
1767
- else :
1768
- if hasattr ( module , "plot" ) :
1769
- # Validate that the interface is implemented when the option
1770
- # is set, rather than at plot time.
1771
- _backends [ backend ] = module
1772
- return module
1776
+
1777
+ if found_backend :
1778
+ if hasattr ( module , "plot" ):
1779
+ # Validate that the interface is implemented when the option is set,
1780
+ # rather than at plot time.
1781
+ return module
1773
1782
1774
1783
raise ValueError (
1775
- f"Could not find plotting backend '{ backend } '. Ensure that you've installed "
1776
- f"the package providing the '{ backend } ' entrypoint, or that the package has a "
1777
- "top-level `.plot` method."
1784
+ f"Could not find plotting backend '{ backend } '. Ensure that you've "
1785
+ f"installed the package providing the '{ backend } ' entrypoint, or that "
1786
+ "the package has a top-level `.plot` method."
1778
1787
)
1779
1788
1780
1789
1781
- def _get_plot_backend (backend = None ):
1790
+ def _get_plot_backend (backend : str | None = None ):
1782
1791
"""
1783
1792
Return the plotting backend to use (e.g. `pandas.plotting._matplotlib`).
1784
1793
1785
- The plotting system of pandas has been using matplotlib, but the idea here
1786
- is that it can also work with other third-party backends. In the future,
1787
- this function will return the backend from a pandas option, and all the
1788
- rest of the code in this file will use the backend specified there for the
1789
- plotting.
1794
+ The plotting system of pandas uses matplotlib by default, but the idea here
1795
+ is that it can also work with other third-party backends. This function
1796
+ returns the module which provides a top-level `.plot` method that will
1797
+ actually do the plotting. The backend is specified from a string, which
1798
+ either comes from the keyword argument `backend`, or, if not specified, from
1799
+ the option `pandas.options.plotting.backend`. All the rest of the code in
1800
+ this file uses the backend specified there for the plotting.
1790
1801
1791
1802
The backend is imported lazily, as matplotlib is a soft dependency, and
1792
1803
pandas can be used without it being installed.
1804
+
1805
+ Notes
1806
+ -----
1807
+ Modifies `_backends` with imported backend as a side effect.
1793
1808
"""
1794
1809
backend = backend or get_option ("plotting.backend" )
1795
1810
1796
- if backend == "matplotlib" :
1797
- # Because matplotlib is an optional dependency and first-party backend,
1798
- # we need to attempt an import here to raise an ImportError if needed.
1799
- try :
1800
- import pandas .plotting ._matplotlib as module
1801
- except ImportError :
1802
- raise ImportError (
1803
- "matplotlib is required for plotting when the "
1804
- 'default backend "matplotlib" is selected.'
1805
- ) from None
1806
-
1807
- _backends ["matplotlib" ] = module
1808
-
1809
1811
if backend in _backends :
1810
1812
return _backends [backend ]
1811
1813
1812
- module = _find_backend (backend )
1814
+ module = _load_backend (backend )
1813
1815
_backends [backend ] = module
1814
1816
return module
0 commit comments