2727from importlib import import_module
2828from importlib .abc import MetaPathFinder
2929from itertools import starmap
30- from re import Match
3130from typing import Any , cast
3231
3332from . import _meta
@@ -135,6 +134,12 @@ def valid(line: str):
135134 return line and not line .startswith ('#' )
136135
137136
137+ class _EntryPointMatch (types .SimpleNamespace ):
138+ module : str
139+ attr : str
140+ extras : str
141+
142+
138143class EntryPoint :
139144 """An entry point as defined by Python packaging conventions.
140145
@@ -150,6 +155,30 @@ class EntryPoint:
150155 'attr'
151156 >>> ep.extras
152157 ['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+
153182 """
154183
155184 pattern = re .compile (
@@ -181,34 +210,40 @@ class EntryPoint:
181210
182211 def __init__ (self , name : str , value : str , group : str ) -> None :
183212 vars (self ).update (name = name , value = value , group = group )
213+ self .module
184214
185215 def load (self ) -> Any :
186216 """Load the entry point from its definition. If only a module
187217 is indicated by the value, return that module. Otherwise,
188218 return the named object.
189219 """
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 ('.' ))
193222 return functools .reduce (getattr , attrs , module )
194223
195224 @property
196225 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
200227
201228 @property
202229 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
206231
207232 @property
208233 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 :
209238 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 ())
212247
213248 def _for (self , dist ):
214249 vars (self ).update (dist = dist )
0 commit comments