1
+ from __future__ import annotations
1
2
import inspect
2
- import sys
3
3
import types
4
4
import warnings
5
5
from typing import (
6
6
Any ,
7
7
Callable ,
8
8
cast ,
9
- Dict ,
10
9
Iterable ,
11
- List ,
12
10
Mapping ,
13
- Optional ,
14
11
Sequence ,
15
- Set ,
16
- Tuple ,
17
12
TYPE_CHECKING ,
18
- Union ,
19
13
)
20
14
21
15
from . import _tracing
16
+ from ._importlib import DistFacade , iter_entrypoint_loaders
22
17
from ._result import _Result
23
18
from ._callers import _multicall
24
19
from ._hooks import (
33
28
_Plugin ,
34
29
)
35
30
36
- if sys .version_info >= (3 , 8 ):
37
- from importlib import metadata as importlib_metadata
38
- else :
39
- import importlib_metadata
40
-
41
31
if TYPE_CHECKING :
42
32
from typing_extensions import Final
43
33
@@ -68,24 +58,6 @@ def __init__(self, plugin: _Plugin, message: str) -> None:
68
58
self .plugin = plugin
69
59
70
60
71
- class DistFacade :
72
- """Emulate a pkg_resources Distribution"""
73
-
74
- def __init__ (self , dist : importlib_metadata .Distribution ) -> None :
75
- self ._dist = dist
76
-
77
- @property
78
- def project_name (self ) -> str :
79
- name : str = self .metadata ["name" ]
80
- return name
81
-
82
- def __getattr__ (self , attr : str , default = None ):
83
- return getattr (self ._dist , attr , default )
84
-
85
- def __dir__ (self ) -> List [str ]:
86
- return sorted (dir (self ._dist ) + ["_dist" , "project_name" ])
87
-
88
-
89
61
class PluginManager :
90
62
"""Core class which manages registration of plugin objects and 1:N hook
91
63
calling.
@@ -111,11 +83,11 @@ class PluginManager:
111
83
)
112
84
113
85
def __init__ (self , project_name : str ) -> None :
114
- self .project_name : " Final" = project_name
115
- self ._name2plugin : " Final[Dict [str, _Plugin]]" = {}
116
- self ._plugin_distinfo : " Final[List[Tuple [_Plugin, DistFacade]]]" = []
117
- self .trace : " Final" = _tracing .TagTracer ().get ("pluginmanage" )
118
- self .hook : " Final" = _HookRelay ()
86
+ self .project_name : Final = project_name
87
+ self ._name2plugin : Final [dict [str , _Plugin ]] = {}
88
+ self ._plugin_distinfo : Final [list [ tuple [_Plugin , DistFacade ]]] = []
89
+ self .trace : Final = _tracing .TagTracer ().get ("pluginmanage" )
90
+ self .hook : Final = _HookRelay ()
119
91
self ._inner_hookexec = _multicall
120
92
121
93
def _hookexec (
@@ -124,12 +96,12 @@ def _hookexec(
124
96
methods : Sequence [HookImpl ],
125
97
kwargs : Mapping [str , object ],
126
98
firstresult : bool ,
127
- ) -> Union [ object , List [object ] ]:
99
+ ) -> object | list [object ]:
128
100
# called from all hookcaller instances.
129
101
# enable_tracing will set its own wrapping function at self._inner_hookexec
130
102
return self ._inner_hookexec (hook_name , methods , kwargs , firstresult )
131
103
132
- def register (self , plugin : _Plugin , name : Optional [ str ] = None ) -> Optional [ str ] :
104
+ def register (self , plugin : _Plugin , name : str | None = None ) -> str | None :
133
105
"""Register a plugin and return its name.
134
106
135
107
If a name is not specified, a name is generated using
@@ -167,7 +139,7 @@ def register(self, plugin: _Plugin, name: Optional[str] = None) -> Optional[str]
167
139
method : _HookImplFunction [object ] = getattr (plugin , name )
168
140
hookimpl = HookImpl (plugin , plugin_name , method , hookimpl_opts )
169
141
name = hookimpl_opts .get ("specname" ) or name
170
- hook : Optional [ _HookCaller ] = getattr (self .hook , name , None )
142
+ hook : _HookCaller | None = getattr (self .hook , name , None )
171
143
if hook is None :
172
144
hook = _HookCaller (name , self ._hookexec )
173
145
setattr (self .hook , name , hook )
@@ -177,25 +149,20 @@ def register(self, plugin: _Plugin, name: Optional[str] = None) -> Optional[str]
177
149
hook ._add_hookimpl (hookimpl )
178
150
return plugin_name
179
151
180
- def parse_hookimpl_opts (
181
- self , plugin : _Plugin , name : str
182
- ) -> Optional ["_HookImplOpts" ]:
152
+ def parse_hookimpl_opts (self , plugin : _Plugin , name : str ) -> _HookImplOpts | None :
183
153
method : object = getattr (plugin , name )
184
154
if not inspect .isroutine (method ):
185
155
return None
186
156
try :
187
- res : Optional [ " _HookImplOpts" ] = getattr (
157
+ res : _HookImplOpts | None = getattr (
188
158
method , self .project_name + "_impl" , None
189
159
)
190
160
except Exception :
191
161
res = {} # type: ignore[assignment]
192
- if res is not None and not isinstance (res , dict ):
193
- # false positive
194
- res = None
195
162
return res
196
163
197
164
def unregister (
198
- self , plugin : Optional [ _Plugin ] = None , name : Optional [ str ] = None
165
+ self , plugin : _Plugin | None = None , name : str | None = None
199
166
) -> _Plugin :
200
167
"""Unregister a plugin and all of its hook implementations.
201
168
@@ -241,7 +208,7 @@ def add_hookspecs(self, module_or_class: _Namespace) -> None:
241
208
for name in dir (module_or_class ):
242
209
spec_opts = self .parse_hookspec_opts (module_or_class , name )
243
210
if spec_opts is not None :
244
- hc : Optional [ _HookCaller ] = getattr (self .hook , name , None )
211
+ hc : _HookCaller | None = getattr (self .hook , name , None )
245
212
if hc is None :
246
213
hc = _HookCaller (name , self ._hookexec , module_or_class , spec_opts )
247
214
setattr (self .hook , name , hc )
@@ -259,14 +226,12 @@ def add_hookspecs(self, module_or_class: _Namespace) -> None:
259
226
260
227
def parse_hookspec_opts (
261
228
self , module_or_class : _Namespace , name : str
262
- ) -> Optional [ " _HookSpecOpts" ] :
229
+ ) -> _HookSpecOpts | None :
263
230
method : HookSpec = getattr (module_or_class , name )
264
- opts : Optional [_HookSpecOpts ] = getattr (
265
- method , self .project_name + "_spec" , None
266
- )
231
+ opts : _HookSpecOpts | None = getattr (method , self .project_name + "_spec" , None )
267
232
return opts
268
233
269
- def get_plugins (self ) -> Set [Any ]:
234
+ def get_plugins (self ) -> set [Any ]:
270
235
"""Return a set of all registered plugin objects."""
271
236
return set (self ._name2plugin .values ())
272
237
@@ -282,18 +247,18 @@ def get_canonical_name(self, plugin: _Plugin) -> str:
282
247
To obtain the name of n registered plugin use :meth:`get_name(plugin)
283
248
<get_name>` instead.
284
249
"""
285
- name : Optional [ str ] = getattr (plugin , "__name__" , None )
250
+ name : str | None = getattr (plugin , "__name__" , None )
286
251
return name or str (id (plugin ))
287
252
288
- def get_plugin (self , name : str ) -> Optional [ Any ] :
253
+ def get_plugin (self , name : str ) -> Any | None :
289
254
"""Return the plugin registered under the given name, if any."""
290
255
return self ._name2plugin .get (name )
291
256
292
257
def has_plugin (self , name : str ) -> bool :
293
258
"""Return whether a plugin with the given name is registered."""
294
259
return self .get_plugin (name ) is not None
295
260
296
- def get_name (self , plugin : _Plugin ) -> Optional [ str ] :
261
+ def get_name (self , plugin : _Plugin ) -> str | None :
297
262
"""Return the name the plugin is registered under, or ``None`` if
298
263
is isn't."""
299
264
for name , val in self ._name2plugin .items ():
@@ -353,9 +318,7 @@ def check_pending(self) -> None:
353
318
% (name , hookimpl .plugin ),
354
319
)
355
320
356
- def load_setuptools_entrypoints (
357
- self , group : str , name : Optional [str ] = None
358
- ) -> int :
321
+ def load_setuptools_entrypoints (self , group : str , name : str | None = None ) -> int :
359
322
"""Load modules from querying the specified setuptools ``group``.
360
323
361
324
:param str group: Entry point group to load plugins.
@@ -364,32 +327,26 @@ def load_setuptools_entrypoints(
364
327
:return: The number of plugins loaded by this call.
365
328
"""
366
329
count = 0
367
- for dist in list (importlib_metadata .distributions ()):
368
- for ep in dist .entry_points :
369
- if (
370
- ep .group != group
371
- or (name is not None and ep .name != name )
372
- # already registered
373
- or self .get_plugin (ep .name )
374
- or self .is_blocked (ep .name )
375
- ):
376
- continue
377
- plugin = ep .load ()
378
- self .register (plugin , name = ep .name )
379
- self ._plugin_distinfo .append ((plugin , DistFacade (dist )))
380
- count += 1
330
+ for dist , ep_name , loader in iter_entrypoint_loaders (group , name ):
331
+ if self .get_plugin (ep_name ) or self .is_blocked (ep_name ):
332
+ continue
333
+ # already registered
334
+ plugin = loader ()
335
+ self .register (plugin , name = ep_name )
336
+ self ._plugin_distinfo .append ((plugin , dist ))
337
+ count += 1
381
338
return count
382
339
383
- def list_plugin_distinfo (self ) -> List [ Tuple [_Plugin , DistFacade ]]:
340
+ def list_plugin_distinfo (self ) -> list [ tuple [_Plugin , DistFacade ]]:
384
341
"""Return a list of (plugin, distinfo) pairs for all
385
342
setuptools-registered plugins."""
386
343
return list (self ._plugin_distinfo )
387
344
388
- def list_name_plugin (self ) -> List [ Tuple [str , _Plugin ]]:
345
+ def list_name_plugin (self ) -> list [ tuple [str , _Plugin ]]:
389
346
"""Return a list of (name, plugin) pairs for all registered plugins."""
390
347
return list (self ._name2plugin .items ())
391
348
392
- def get_hookcallers (self , plugin : _Plugin ) -> Optional [ List [ _HookCaller ]] :
349
+ def get_hookcallers (self , plugin : _Plugin ) -> list [ _HookCaller ] | None :
393
350
"""Get all hook callers for the specified plugin."""
394
351
if self .get_name (plugin ) is None :
395
352
return None
@@ -422,7 +379,7 @@ def traced_hookexec(
422
379
hook_impls : Sequence [HookImpl ],
423
380
caller_kwargs : Mapping [str , object ],
424
381
firstresult : bool ,
425
- ) -> Union [ object , List [object ] ]:
382
+ ) -> object | list [object ]:
426
383
before (hook_name , hook_impls , caller_kwargs )
427
384
outcome = _Result .from_call (
428
385
lambda : oldcall (hook_name , hook_impls , caller_kwargs , firstresult )
0 commit comments