Skip to content

Commit 426b929

Browse files
committed
Address review comments
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent 6381577 commit 426b929

File tree

3 files changed

+45
-82
lines changed

3 files changed

+45
-82
lines changed

src/python_inspector/resolution.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def get_environment_marker_from_environment(environment):
140140

141141
def contain_string(string: str, files: List) -> bool:
142142
"""
143-
Return True if the string is contains in any of the files.
143+
Return True if the ``string`` is contained in any of the ``files`` list of file paths.
144144
"""
145145
for file in files:
146146
if not os.path.exists(file):
@@ -154,7 +154,7 @@ def contain_string(string: str, files: List) -> bool:
154154

155155
def parse_reqs_from_setup_py_insecurely(setup_py):
156156
"""
157-
Yield requirements from the setup.py file at ``setup_py``.
157+
Yield Requirement(s) from a ``setup_py`` setup.py file location .
158158
"""
159159
if not os.path.exists(setup_py):
160160
return []
@@ -164,7 +164,7 @@ def parse_reqs_from_setup_py_insecurely(setup_py):
164164

165165
def parse_deps_from_setup_py_insecurely(setup_py):
166166
"""
167-
Yield requirements from the setup.py file at ``setup_py``.
167+
Yield DependentPackage(s) from the ``setup_py`` setup.py file location .
168168
"""
169169
if not os.path.exists(setup_py):
170170
return []
@@ -668,7 +668,11 @@ def get_package_list(results):
668668

669669
def get_setup_dependencies(location, analyze_setup_py_insecurely=False, use_requirements=True):
670670
"""
671-
Yield dependencies from the given setup.py and setup.cfg location.
671+
Yield Requirement(s) from Pypi in the ``location`` directory that contains
672+
a setup.py and/or a setup.cfg and optionally a requirements.txt file if
673+
``use_requirements`` is True and this file is used in the setup.py or setup.cfg.
674+
Perform an insecure live evaluation of the Python code if needed and if
675+
``analyze_setup_py_insecurely`` is True.
672676
"""
673677

674678
setup_py_location = os.path.join(

src/python_inspector/setup_py_live_eval.py

Lines changed: 36 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
#
1010
"""Generate requirements from `setup.py` and `requirements-devel.txt`."""
1111

12-
from __future__ import absolute_import
13-
from __future__ import print_function
14-
1512
import os
1613
import re
1714
import sys
@@ -22,17 +19,9 @@
2219
import ConfigParser as configparser
2320

2421
import mock
25-
import pkg_resources
2622
import setuptools
27-
28-
29-
def parse_set(string):
30-
"""Parse set from comma separated string."""
31-
string = string.strip()
32-
if string:
33-
return set(string.split(","))
34-
else:
35-
return set()
23+
from commoncode.command import pushd
24+
from packaging.requirements import Requirement
3625

3726

3827
def minver_error(pkg_name):
@@ -47,49 +36,8 @@ def minver_error(pkg_name):
4736
def build_pkg_name(pkg):
4837
"""Build package name, including extras if present."""
4938
if pkg.extras:
50-
return "{0}[{1}]".format(pkg.project_name, ",".join(sorted(pkg.extras)))
51-
return pkg.project_name
52-
53-
54-
def parse_pip_file(path):
55-
"""Parse pip requirements file."""
56-
# requirement lines sorted by importance
57-
# also collect other pip commands
58-
rdev = {}
59-
rnormal = []
60-
stuff = []
61-
62-
try:
63-
with open(path) as f:
64-
for line in f:
65-
line = line.strip()
66-
67-
# see https://pip.readthedocs.io/en/1.1/requirements.html
68-
if line.startswith("-e"):
69-
# devel requirement
70-
splitted = line.split("#egg=")
71-
rdev[splitted[1].lower()] = line
72-
73-
elif line.startswith("-r"):
74-
# recursive file command
75-
splitted = re.split("-r\\s+", line)
76-
subrdev, subrnormal, substuff = parse_pip_file(
77-
os.path.join(os.path.dirname(path), splitted[1])
78-
)
79-
for k, v in subrdev.items():
80-
if k not in rdev:
81-
rdev[k] = v
82-
rnormal.extend(subrnormal)
83-
elif line.startswith("-"):
84-
# another special command we don't recognize
85-
stuff.append(line)
86-
else:
87-
# ordinary requirement, similarly to them used in setup.py
88-
rnormal.append(line)
89-
except IOError:
90-
print('Warning: could not parse requirements file "{0}"!'.format(path), file=sys.stderr)
91-
92-
return rdev, rnormal, stuff
39+
return "{0}[{1}]".format(str(pkg.name), ",".join(sorted(pkg.extras)))
40+
return str(pkg.name)
9341

9442

9543
def iter_requirements(level, extras, setup_file):
@@ -100,24 +48,26 @@ def iter_requirements(level, extras, setup_file):
10048
result = dict()
10149
requires = []
10250
stuff = []
103-
cd = os.getcwd()
104-
os.chdir(os.path.dirname(setup_file))
10551
install_requires = []
10652
requires_extras = {}
53+
test_requires = {}
54+
setup_requires = {}
10755
# change directory to setup.py path
108-
with mock.patch.object(setuptools, "setup") as mock_setup:
109-
sys.path.append(os.path.dirname(setup_file))
110-
g = {"__file__": setup_file, "__name__": "__main__"}
111-
with open(setup_file) as sf:
112-
exec(sf.read(), g)
113-
sys.path.pop()
114-
assert g["setup"] # silence warning about unused imports
56+
with pushd(os.path.dirname(setup_file)):
57+
with mock.patch.object(setuptools, "setup") as mock_setup:
58+
sys.path.append(os.path.dirname(setup_file))
59+
g = {"__file__": setup_file, "__name__": "__main__"}
60+
with open(setup_file) as sf:
61+
exec(sf.read(), g)
62+
sys.path.pop()
63+
assert g["setup"] # silence warning about unused imports
11564
# called arguments are in `mock_setup.call_args`
116-
os.chdir(cd)
11765
mock_args, mock_kwargs = mock_setup.call_args
11866
install_requires = mock_kwargs.get("install_requires", install_requires)
11967

12068
requires_extras = mock_kwargs.get("extras_require", requires_extras)
69+
test_requires = mock_kwargs.get("test_requires", test_requires)
70+
setup_requires = mock_kwargs.get("setup_requires", setup_requires)
12171

12272
for e, reqs in requires_extras.items():
12373
# Handle conditions on extras. See pkginfo_to_metadata function
@@ -130,10 +80,18 @@ def iter_requirements(level, extras, setup_file):
13080
reqs = ["{0}; {1}".format(r, condition) for r in reqs]
13181
install_requires.extend(reqs)
13282

133-
for pkg in pkg_resources.parse_requirements(install_requires):
83+
for reqs in test_requires:
84+
if "test" in extras:
85+
install_requires.extend(reqs)
86+
87+
for reqs in setup_requires:
88+
if "setup" in extras:
89+
install_requires.extend(reqs)
90+
91+
for req in install_requires:
13492
# skip things we already know
13593
# FIXME be smarter about merging things
136-
94+
pkg = Requirement(req)
13795
# Evaluate environment markers skip if not applicable
13896
if hasattr(pkg, "marker") and pkg.marker is not None:
13997
if not pkg.marker.evaluate():
@@ -142,10 +100,11 @@ def iter_requirements(level, extras, setup_file):
142100
# Remove markers from the output
143101
pkg.marker = None
144102

145-
if pkg.key in result:
103+
if pkg.name in result:
146104
continue
147105

148-
specs = dict(pkg.specs)
106+
specs = pkg.specifier
107+
specs = {s.operator: s.version for s in specs._specs}
149108
if ((">=" in specs) and (">" in specs)) or (("<=" in specs) and ("<" in specs)):
150109
print(
151110
"ERROR: Do not specify such weird constraints! " '("{0}")'.format(pkg),
@@ -154,32 +113,32 @@ def iter_requirements(level, extras, setup_file):
154113
sys.exit(1)
155114

156115
if "==" in specs:
157-
result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs["=="])
116+
result[pkg.name] = "{0}=={1}".format(build_pkg_name(pkg), specs["=="])
158117

159118
elif ">=" in specs:
160119
if level == "min":
161-
result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs[">="])
120+
result[pkg.name] = "{0}=={1}".format(build_pkg_name(pkg), specs[">="])
162121
else:
163-
result[pkg.key] = pkg
122+
result[pkg.name] = pkg
164123

165124
elif ">" in specs:
166125
if level == "min":
167126
minver_error(build_pkg_name(pkg))
168127
else:
169-
result[pkg.key] = pkg
128+
result[pkg.name] = pkg
170129

171130
elif "~=" in specs:
172131
if level == "min":
173-
result[pkg.key] = "{0}=={1}".format(build_pkg_name(pkg), specs["~="])
132+
result[pkg.name] = "{0}=={1}".format(build_pkg_name(pkg), specs["~="])
174133
else:
175134
ver, _ = os.path.splitext(specs["~="])
176-
result[pkg.key] = "{0}>={1},=={2}.*".format(build_pkg_name(pkg), specs["~="], ver)
135+
result[pkg.name] = "{0}>={1},=={2}.*".format(build_pkg_name(pkg), specs["~="], ver)
177136

178137
else:
179138
if level == "min":
180139
minver_error(build_pkg_name(pkg))
181140
else:
182-
result[pkg.key] = build_pkg_name(pkg)
141+
result[pkg.name] = build_pkg_name(pkg)
183142

184143
for s in stuff:
185144
yield s

tests/test_resolution.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def test_without_supported_wheels():
147147
"pkg:pypi/hyperlink@21.0.0",
148148
"pkg:pypi/idna@3.4",
149149
"pkg:pypi/pycparser@2.21",
150-
"pkg:pypi/setuptools@65.3.0",
150+
"pkg:pypi/setuptools@65.4.0",
151151
"pkg:pypi/txaio@22.2.1",
152152
]
153153

0 commit comments

Comments
 (0)