27
27
from importlib import import_module
28
28
from importlib .abc import MetaPathFinder
29
29
from itertools import starmap
30
- from re import Match
31
30
from typing import Any , cast
32
31
33
32
from . import _meta
@@ -135,6 +134,12 @@ def valid(line: str):
135
134
return line and not line .startswith ('#' )
136
135
137
136
137
+ class _EntryPointMatch (types .SimpleNamespace ):
138
+ module : str
139
+ attr : str
140
+ extras : str
141
+
142
+
138
143
class EntryPoint :
139
144
"""An entry point as defined by Python packaging conventions.
140
145
@@ -150,6 +155,30 @@ class EntryPoint:
150
155
'attr'
151
156
>>> ep.extras
152
157
['extra1', 'extra2']
158
+
159
+ If the value package or module are not valid identifiers, a
160
+ ValueError is raised on access.
161
+
162
+ >>> EntryPoint(name=None, group=None, value='invalid-name').module
163
+ Traceback (most recent call last):
164
+ ...
165
+ ValueError: ('Invalid object reference...invalid-name...
166
+ >>> EntryPoint(name=None, group=None, value='invalid-name').attr
167
+ Traceback (most recent call last):
168
+ ...
169
+ ValueError: ('Invalid object reference...invalid-name...
170
+ >>> EntryPoint(name=None, group=None, value='invalid-name').extras
171
+ Traceback (most recent call last):
172
+ ...
173
+ ValueError: ('Invalid object reference...invalid-name...
174
+
175
+ The same thing happens on construction.
176
+
177
+ >>> EntryPoint(name=None, group=None, value='invalid-name')
178
+ Traceback (most recent call last):
179
+ ...
180
+ ValueError: ('Invalid object reference...invalid-name...
181
+
153
182
"""
154
183
155
184
pattern = re .compile (
@@ -181,34 +210,40 @@ class EntryPoint:
181
210
182
211
def __init__ (self , name : str , value : str , group : str ) -> None :
183
212
vars (self ).update (name = name , value = value , group = group )
213
+ self .module
184
214
185
215
def load (self ) -> Any :
186
216
"""Load the entry point from its definition. If only a module
187
217
is indicated by the value, return that module. Otherwise,
188
218
return the named object.
189
219
"""
190
- match = cast (Match , self .pattern .match (self .value ))
191
- module = import_module (match .group ('module' ))
192
- attrs = filter (None , (match .group ('attr' ) or '' ).split ('.' ))
220
+ module = import_module (self .module )
221
+ attrs = filter (None , (self .attr or '' ).split ('.' ))
193
222
return functools .reduce (getattr , attrs , module )
194
223
195
224
@property
196
225
def module (self ) -> str :
197
- match = self .pattern .match (self .value )
198
- assert match is not None
199
- return match .group ('module' )
226
+ return self ._match .module
200
227
201
228
@property
202
229
def attr (self ) -> str :
203
- match = self .pattern .match (self .value )
204
- assert match is not None
205
- return match .group ('attr' )
230
+ return self ._match .attr
206
231
207
232
@property
208
233
def extras (self ) -> list [str ]:
234
+ return re .findall (r'\w+' , self ._match .extras or '' )
235
+
236
+ @functools .cached_property
237
+ def _match (self ) -> _EntryPointMatch :
209
238
match = self .pattern .match (self .value )
210
- assert match is not None
211
- return re .findall (r'\w+' , match .group ('extras' ) or '' )
239
+ if not match :
240
+ raise ValueError (
241
+ 'Invalid object reference. '
242
+ 'See https://packaging.python.org'
243
+ '/en/latest/specifications/entry-points/#data-model' ,
244
+ self .value ,
245
+ )
246
+ return _EntryPointMatch (** match .groupdict ())
212
247
213
248
def _for (self , dist ):
214
249
vars (self ).update (dist = dist )
0 commit comments