Skip to content

Commit 9962904

Browse files
ENH: Support plugin DataFrame accessor via entry points (#29076)
Allows external libraries to register DataFrame accessors using the 'pandas_dataframe_accessor' entry point group. This enables plugins to be automatically used without explicit import. Co-authored-by: Afonso Antunes <[email protected]>
1 parent f496acf commit 9962904

File tree

3 files changed

+65
-0
lines changed

3 files changed

+65
-0
lines changed

pandas/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,7 @@
346346
"unique",
347347
"wide_to_long",
348348
]
349+
350+
from pandas.core.accessor import DataFrameAccessorLoader
351+
352+
DataFrameAccessorLoader.load()

pandas/core/accessor.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
from pandas.core.generic import NDFrame
2727

2828

29+
from importlib_metadata import entry_points
30+
31+
2932
class DirNamesMixin:
3033
_accessors: set[str] = set()
3134
_hidden_attrs: frozenset[str] = frozenset()
@@ -393,3 +396,26 @@ def register_index_accessor(name: str) -> Callable[[TypeT], TypeT]:
393396
from pandas import Index
394397

395398
return _register_accessor(name, Index)
399+
400+
401+
class DataFrameAccessorLoader:
402+
"""Loader class for registering DataFrame accessors via entry points."""
403+
404+
ENTRY_POINT_GROUP = "pandas_dataframe_accessor"
405+
406+
@classmethod
407+
def load(cls):
408+
"""loads and registers accessors defined by 'pandas_dataframe_accessor'."""
409+
eps = entry_points(group=cls.ENTRY_POINT_GROUP)
410+
411+
for ep in eps:
412+
name = ep.name
413+
414+
def make_property(ep):
415+
def accessor(self):
416+
cls_ = ep.load()
417+
return cls_(self)
418+
419+
return accessor
420+
421+
register_dataframe_accessor(name)(make_property(ep))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import pandas as pd
2+
from pandas.core.accessor import DataFrameAccessorLoader
3+
4+
5+
def test_load_dataframe_accessors(monkeypatch):
6+
# GH29076
7+
# Mocked EntryPoint to simulate a plugin
8+
class MockEntryPoint:
9+
name = "test_accessor"
10+
11+
def load(self):
12+
class TestAccessor:
13+
def __init__(self, df):
14+
self._df = df
15+
16+
def test_method(self):
17+
return "success"
18+
19+
return TestAccessor
20+
21+
# Mock entry_points
22+
def mock_entry_points(*, group):
23+
if group == DataFrameAccessorLoader.ENTRY_POINT_GROUP:
24+
return [MockEntryPoint()]
25+
return []
26+
27+
# Patch entry_points in the correct module
28+
monkeypatch.setattr("pandas.core.accessor.entry_points", mock_entry_points)
29+
30+
DataFrameAccessorLoader.load()
31+
32+
# Create DataFrame and verify that the accessor was registered
33+
df = pd.DataFrame({"a": [1, 2, 3]})
34+
assert hasattr(df, "test_accessor")
35+
assert df.test_accessor.test_method() == "success"

0 commit comments

Comments
 (0)