Skip to content

Commit d354f83

Browse files
authored
be more pessimistic about reading setup.py (#9000)
1 parent cff4d7d commit d354f83

File tree

2 files changed

+62
-67
lines changed

2 files changed

+62
-67
lines changed

src/poetry/utils/setup_reader.py

+59-61
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
from pathlib import Path
1515

1616

17+
class SetupReaderError(Exception):
18+
pass
19+
20+
1721
class SetupReader:
1822
"""
1923
Class that reads a setup.py file without executing it.
@@ -192,113 +196,95 @@ def _find_sub_setup_call(
192196
return None
193197

194198
def _find_install_requires(self, call: ast.Call, body: list[ast.stmt]) -> list[str]:
195-
install_requires: list[str] = []
196199
value = self._find_in_call(call, "install_requires")
197200
if value is None:
198201
# Trying to find in kwargs
199202
kwargs = self._find_call_kwargs(call)
200203

201204
if kwargs is None or not isinstance(kwargs, ast.Name):
202-
return install_requires
205+
return []
203206

204207
variable = self._find_variable_in_body(body, kwargs.id)
205-
if not isinstance(variable, (ast.Dict, ast.Call)):
206-
return install_requires
207-
208-
if isinstance(variable, ast.Call):
209-
if not isinstance(variable.func, ast.Name):
210-
return install_requires
211208

212-
if variable.func.id != "dict":
213-
return install_requires
209+
if isinstance(variable, ast.Dict):
210+
value = self._find_in_dict(variable, "install_requires")
214211

212+
elif (
213+
isinstance(variable, ast.Call)
214+
and isinstance(variable.func, ast.Name)
215+
and variable.func.id == "dict"
216+
):
215217
value = self._find_in_call(variable, "install_requires")
218+
216219
else:
217-
value = self._find_in_dict(variable, "install_requires")
220+
raise SetupReaderError(f"Cannot handle variable {variable}")
218221

219222
if value is None:
220-
return install_requires
223+
return []
221224

222-
if isinstance(value, ast.List):
223-
for el in value.elts:
224-
if isinstance(el, ast.Constant) and isinstance(el.value, str):
225-
install_requires.append(el.value)
226-
elif isinstance(value, ast.Name):
227-
variable = self._find_variable_in_body(body, value.id)
225+
if isinstance(value, ast.Name):
226+
value = self._find_variable_in_body(body, value.id)
228227

229-
if variable is not None and isinstance(variable, ast.List):
230-
for el in variable.elts:
231-
if isinstance(el, ast.Constant) and isinstance(el.value, str):
232-
install_requires.append(el.value)
228+
if isinstance(value, ast.Constant) and value.value is None:
229+
return []
233230

234-
return install_requires
231+
if isinstance(value, ast.List):
232+
return string_list_values(value)
233+
234+
raise SetupReaderError(f"Cannot handle value of type {type(value)}")
235235

236236
def _find_extras_require(
237237
self, call: ast.Call, body: list[ast.stmt]
238238
) -> dict[str, list[str]]:
239-
extras_require: dict[str, list[str]] = {}
240239
value = self._find_in_call(call, "extras_require")
241240
if value is None:
242241
# Trying to find in kwargs
243242
kwargs = self._find_call_kwargs(call)
244243

245244
if kwargs is None or not isinstance(kwargs, ast.Name):
246-
return extras_require
245+
return {}
247246

248247
variable = self._find_variable_in_body(body, kwargs.id)
249-
if not isinstance(variable, (ast.Dict, ast.Call)):
250-
return extras_require
251-
252-
if isinstance(variable, ast.Call):
253-
if not isinstance(variable.func, ast.Name):
254-
return extras_require
255-
256-
if variable.func.id != "dict":
257-
return extras_require
248+
if isinstance(variable, ast.Dict):
249+
value = self._find_in_dict(variable, "extras_require")
258250

251+
elif (
252+
isinstance(variable, ast.Call)
253+
and isinstance(variable.func, ast.Name)
254+
and variable.func.id == "dict"
255+
):
259256
value = self._find_in_call(variable, "extras_require")
257+
260258
else:
261-
value = self._find_in_dict(variable, "extras_require")
259+
raise SetupReaderError(f"Cannot handle variable {variable}")
262260

263261
if value is None:
264-
return extras_require
262+
return {}
263+
264+
if isinstance(value, ast.Name):
265+
value = self._find_variable_in_body(body, value.id)
266+
267+
if isinstance(value, ast.Constant) and value.value is None:
268+
return {}
265269

266270
if isinstance(value, ast.Dict):
271+
extras_require: dict[str, list[str]] = {}
267272
val: ast.expr | None
268273
for key, val in zip(value.keys, value.values):
269274
if not isinstance(key, ast.Constant) or not isinstance(key.value, str):
270-
continue
275+
raise SetupReaderError(f"Cannot handle key {key}")
271276

272277
if isinstance(val, ast.Name):
273278
val = self._find_variable_in_body(body, val.id)
274279

275-
if isinstance(val, ast.List):
276-
extras_require[key.value] = [
277-
e.value
278-
for e in val.elts
279-
if isinstance(e, ast.Constant) and isinstance(e.value, str)
280-
]
281-
elif isinstance(value, ast.Name):
282-
variable = self._find_variable_in_body(body, value.id)
283-
284-
if variable is None or not isinstance(variable, ast.Dict):
285-
return extras_require
286-
287-
for key, val in zip(variable.keys, variable.values):
288-
if not isinstance(key, ast.Constant) or not isinstance(key.value, str):
289-
continue
280+
if not isinstance(val, ast.List):
281+
raise SetupReaderError(f"Cannot handle value of type {type(val)}")
290282

291-
if isinstance(val, ast.Name):
292-
val = self._find_variable_in_body(body, val.id)
283+
extras_require[key.value] = string_list_values(val)
293284

294-
if isinstance(val, ast.List):
295-
extras_require[key.value] = [
296-
e.value
297-
for e in val.elts
298-
if isinstance(e, ast.Constant) and isinstance(e.value, str)
299-
]
285+
return extras_require
300286

301-
return extras_require
287+
raise SetupReaderError(f"Cannot handle value of type {type(value)}")
302288

303289
def _find_single_string(
304290
self, call: ast.Call, body: list[ast.stmt], name: str
@@ -383,3 +369,15 @@ def _find_in_dict(self, dict_: ast.Dict, name: str) -> ast.expr | None:
383369
return val
384370

385371
return None
372+
373+
374+
def string_list_values(value: ast.List) -> list[str]:
375+
strings = []
376+
for element in value.elts:
377+
if isinstance(element, ast.Constant) and isinstance(element.value, str):
378+
strings.append(element.value)
379+
380+
else:
381+
raise SetupReaderError("Found non-string element in list")
382+
383+
return strings

tests/inspection/test_info.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -303,12 +303,9 @@ def test_info_setup_complex_pep517_legacy(
303303
def test_info_setup_complex_disable_build(
304304
mocker: MockerFixture, demo_setup_complex: Path
305305
) -> None:
306-
spy = mocker.spy(VirtualEnv, "run")
307-
info = PackageInfo.from_directory(demo_setup_complex, disable_build=True)
308-
assert spy.call_count == 0
309-
assert info.name == "demo"
310-
assert info.version == "0.1.0"
311-
assert info.requires_dist is None
306+
# Cannot extract install_requires from list comprehension.
307+
with pytest.raises(PackageInfoError):
308+
PackageInfo.from_directory(demo_setup_complex, disable_build=True)
312309

313310

314311
@pytest.mark.network

0 commit comments

Comments
 (0)