Skip to content

Commit b9c4be4

Browse files
committed
Merge pull request #518 from python/bugfix/488-bad-ep-value
Raise consistent ValueError for invalid EntryPoint.value
2 parents c10bdf3 + 9f8af01 commit b9c4be4

File tree

2 files changed

+48
-12
lines changed

2 files changed

+48
-12
lines changed

importlib_metadata/__init__.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
from importlib import import_module
2828
from importlib.abc import MetaPathFinder
2929
from itertools import starmap
30-
from re import Match
3130
from typing import Any, cast
3231

3332
from . 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+
138143
class 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)

newsfragments/518.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Raise consistent ValueError for invalid EntryPoint.value

0 commit comments

Comments
 (0)