Skip to content

Commit 3865359

Browse files
committed
remove pydantic-specific logic and support hookspecs too
1 parent aae3799 commit 3865359

File tree

1 file changed

+25
-12
lines changed

1 file changed

+25
-12
lines changed

src/pluggy/_manager.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ def _warn_for_function(warning: Warning, function: Callable[..., object]) -> Non
4545
filename=func.__code__.co_filename,
4646
)
4747

48+
def _attr_is_property(obj: Any, name: str) -> bool:
49+
"""Check if a given attr is a @property on a module, class, or object"""
50+
if inspect.ismodule(obj):
51+
return False # modules can never have @property methods
52+
53+
base_class = obj if inspect.isclass(obj) else type(obj)
54+
if isinstance(getattr(base_class, name, None), property):
55+
return True
56+
return False
57+
4858

4959
class PluginValidationError(Exception):
5060
"""Plugin failed validation.
@@ -182,23 +192,16 @@ def parse_hookimpl_opts(self, plugin: _Plugin, name: str) -> HookimplOpts | None
182192
options for items decorated with :class:`HookimplMarker`.
183193
"""
184194

185-
# IMPORTANT: @property methods can have side effects, and are never hookimpl
186-
# if attr is a property, skip it in advance
187-
plugin_class = plugin if inspect.isclass(plugin) else type(plugin)
188-
if isinstance(getattr(plugin_class, name, None), property):
189-
return None
190-
191-
# pydantic model fields are like attrs and also can never be hookimpls
192-
plugin_is_pydantic_obj = hasattr(plugin, "__pydantic_core_schema__")
193-
if plugin_is_pydantic_obj and name in getattr(plugin, "model_fields", {}):
195+
if _attr_is_property(plugin, name):
196+
# @property methods can have side effects, and are never hookimpls
194197
return None
195198

196199
method: object
197200
try:
198201
method = getattr(plugin, name)
199202
except AttributeError:
200-
# AttributeError: '__signature__' attribute of 'Plugin' is class-only
201-
# can happen for some special objects (e.g. proxies, pydantic, etc.)
203+
# AttributeError: '__signature__' attribute of 'plugin' is class-only
204+
# can happen if plugin is a proxy object wrapping a class/module
202205
method = getattr(type(plugin), name) # use class sig instead
203206

204207
if not inspect.isroutine(method):
@@ -305,7 +308,17 @@ def parse_hookspec_opts(
305308
customize how hook specifications are picked up. By default, returns the
306309
options for items decorated with :class:`HookspecMarker`.
307310
"""
308-
method = getattr(module_or_class, name)
311+
if _attr_is_property(module_or_class, name):
312+
# @property methods can have side effects, and are never hookspecs
313+
return None
314+
315+
method: object
316+
try:
317+
method = getattr(module_or_class, name)
318+
except AttributeError:
319+
# AttributeError: '__signature__' attribute of <m_or_c> is class-only
320+
# can happen if module_or_class is a proxy obj wrapping a class/module
321+
method = getattr(type(module_or_class), name) # use class sig instead
309322
opts: HookspecOpts | None = getattr(method, self.project_name + "_spec", None)
310323
return opts
311324

0 commit comments

Comments
 (0)