Skip to content

Commit 37d9038

Browse files
authored
Merge pull request #209 from sarugaku/bugfix/204
Fix binary operator mapping in setup.py ast parser
2 parents f956fb8 + 4880031 commit 37d9038

19 files changed

+570
-472
lines changed

.pre-commit-config.yaml

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/ambv/black
3-
rev: 19.10b0
3+
rev: stable
44
hooks:
55
- id: black
66
args: ["--target-version=py27", "--target-version=py37"]
@@ -11,11 +11,18 @@ repos:
1111
#- id: flake8
1212

1313
- repo: https://github.com/asottile/seed-isort-config
14-
rev: v1.9.3
14+
rev: v2.1.0
1515
hooks:
1616
- id: seed-isort-config
17+
args: [
18+
--application-directories=src/requirementslib,
19+
--settings-path=./,
20+
# --exclude=tests/.*\.py,
21+
# --exclude=src/requirementslib/models/setup_info.py
22+
]
1723

18-
- repo: https://github.com/pre-commit/mirrors-isort
19-
rev: v4.3.21
24+
- repo: https://github.com/timothycrosley/isort
25+
rev: 4.3.21
2026
hooks:
2127
- id: isort
28+

Pipfile.lock

+209-264
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

azure-pipelines.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343

4444
- job: TestWindows
4545
pool:
46-
vmImage: windows-2019
46+
vmImage: windows-latest
4747
strategy:
4848
matrix:
4949
Python27:
@@ -54,13 +54,15 @@ jobs:
5454
python.version: '3.6'
5555
Python37:
5656
python.version: '3.7'
57+
Python38:
58+
python.version: '3.8'
5759
maxParallel: 4
5860
steps:
5961
- template: .azure-pipelines/templates/run-tests.yml
6062

6163
- job: TestMacOS
6264
pool:
63-
vmImage: macOS-10.13
65+
vmImage: macOS-latest
6466
strategy:
6567
matrix:
6668
Python27:
@@ -71,6 +73,8 @@ jobs:
7173
python.version: '3.6'
7274
Python37:
7375
python.version: '3.7'
76+
Python38:
77+
python.version: '3.8'
7478
maxParallel: 4
7579
steps:
7680
- template: .azure-pipelines/templates/run-tests.yml

news/204.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed an issue in binary operator mapping in the ``ast_parse_setup_py`` functionality of the dependency parser which could cause dependency resolution to fail.

news/206.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed an issue which caused mappings of binary operators to fail to evaluate when parsing ``setup.py`` files.

news/207.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed mapping and evaluation of boolean operators and comparisons when evaluating ``setup.py`` files with AST parser to discover dependencies.

setup.cfg

+5-5
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,17 @@ install_requires =
5050
attrs>=18.2
5151
cached_property
5252
distlib>=0.2.8
53-
first
5453
orderedmultidict
5554
packaging>=19.0
5655
pep517>=0.5.0
5756
pip-shims>=0.3.2
5857
plette[validation]
58+
python-dateutil
5959
requests
6060
scandir;python_version<"3.5"
6161
setuptools>=40.8
6262
six>=1.11.0
6363
tomlkit>=0.5.3
64-
typing;python_version<"3.5"
6564
vistir>=0.3.1
6665

6766
[options.extras_require]
@@ -89,6 +88,7 @@ tests =
8988
coverage
9089
hypothesis
9190
typing =
91+
typing;python_version<"3.5"
9292
mypy;python_version>="3.4"
9393
mypy-extensions;python_version>="3.4"
9494
mypytools;python_version>="3.4"
@@ -102,8 +102,8 @@ universal = 1
102102

103103
[tool:pytest]
104104
strict = true
105-
plugins = flake8 cov timeout
106-
addopts = -ra --cov -n auto --timeout 300
105+
plugins = flake8 cov xdist timeout
106+
addopts = -ra --cov=requirementslib -n auto --timeout 300
107107
testpaths = tests/
108108
norecursedirs = .* build dist news tasks docs tests/artifacts
109109
flake8-ignore =
@@ -127,7 +127,7 @@ not_skip = __init__.py
127127
line_length = 90
128128
indent = ' '
129129
multi_line_output = 3
130-
known_third_party = appdirs,attr,cached_property,distlib,environ,first,hypothesis,invoke,orderedmultidict,packaging,parver,pep517,pip_shims,pkg_resources,plette,pyparsing,pytest,requests,setuptools,six,tomlkit,towncrier,urllib3,vistir
130+
known_third_party = appdirs,attr,cached_property,distlib,environ,hypothesis,invoke,orderedmultidict,packaging,parver,pep517,pip_shims,pkg_resources,plette,pyparsing,pytest,requests,setuptools,six,tomlkit,towncrier,urllib3,vistir
131131
known_first_party = requirementslib,tests
132132
combine_as_imports=True
133133
include_trailing_comma = True

src/requirementslib/models/dependencies.py

+17-88
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import packaging.version
1212
import pip_shims.shims
1313
import requests
14-
from first import first
1514
from packaging.utils import canonicalize_name
1615
from vistir.compat import JSONDecodeError, fs_str
1716
from vistir.contextmanagers import cd, temp_environ
@@ -20,6 +19,7 @@
2019
from ..environment import MYPY_RUNNING
2120
from ..utils import _ensure_dir, prepare_pip_source_args
2221
from .cache import CACHE_DIR, DependencyCache
22+
from .setup_info import SetupInfo
2323
from .utils import (
2424
clean_requires_python,
2525
fix_requires_python_marker,
@@ -144,9 +144,9 @@ def compatible_versions(self, other):
144144
:rtype: set(str)
145145
"""
146146

147-
if len(self.candidates) == 1 and first(self.candidates).editable:
147+
if len(self.candidates) == 1 and next(iter(self.candidates)).editable:
148148
return self
149-
elif len(other.candidates) == 1 and first(other.candidates).editable:
149+
elif len(other.candidates) == 1 and next(iter(other.candidates)).editable:
150150
return other
151151
return self.version_set & other.version_set
152152

@@ -163,9 +163,9 @@ def compatible_abstract_dep(self, other):
163163

164164
from .requirements import Requirement
165165

166-
if len(self.candidates) == 1 and first(self.candidates).editable:
166+
if len(self.candidates) == 1 and next(iter(self.candidates)).editable:
167167
return self
168-
elif len(other.candidates) == 1 and first(other.candidates).editable:
168+
elif len(other.candidates) == 1 and next(iter(other.candidates)).editable:
169169
return other
170170
new_specifiers = self.specifiers & other.specifiers
171171
markers = set(self.markers) if self.markers else set()
@@ -480,90 +480,19 @@ def get_dependencies_from_index(dep, sources=None, pip_options=None, wheel_cache
480480
if not wheel_cache:
481481
wheel_cache = WHEEL_CACHE
482482
dep.is_direct = True
483-
reqset = pip_shims.shims.RequirementSet()
484-
reqset.add_requirement(dep)
485483
requirements = None
486484
setup_requires = {}
487-
with temp_environ(), start_resolver(
488-
finder=finder, session=session, wheel_cache=wheel_cache
489-
) as resolver:
485+
with temp_environ():
490486
os.environ["PIP_EXISTS_ACTION"] = "i"
491-
dist = None
492487
if dep.editable and not dep.prepared and not dep.req:
493-
with cd(dep.setup_py_dir):
494-
from setuptools.dist import distutils
495-
496-
try:
497-
dist = distutils.core.run_setup(dep.setup_py)
498-
except (ImportError, TypeError, AttributeError):
499-
dist = None
500-
else:
501-
setup_requires[dist.get_name()] = dist.setup_requires
502-
if not dist:
503-
try:
504-
dist = dep.get_dist()
505-
except (TypeError, ValueError, AttributeError):
506-
pass
507-
else:
508-
setup_requires[dist.get_name()] = dist.setup_requires
509-
resolver.require_hashes = False
510-
try:
511-
results = resolver._resolve_one(reqset, dep)
512-
except Exception:
513-
# FIXME: Needs to bubble the exception somehow to the user.
514-
results = []
515-
finally:
516-
try:
517-
wheel_cache.cleanup()
518-
except AttributeError:
519-
pass
520-
resolver_requires_python = getattr(resolver, "requires_python", None)
521-
requires_python = getattr(reqset, "requires_python", resolver_requires_python)
522-
if requires_python:
523-
add_marker = fix_requires_python_marker(requires_python)
524-
reqset.remove(dep)
525-
if dep.req.marker:
526-
dep.req.marker._markers.extend(["and"].extend(add_marker._markers))
527-
else:
528-
dep.req.marker = add_marker
529-
reqset.add(dep)
530-
requirements = set()
531-
for r in results:
532-
if requires_python:
533-
if r.req.marker:
534-
r.req.marker._markers.extend(["and"].extend(add_marker._markers))
535-
else:
536-
r.req.marker = add_marker
537-
requirements.add(format_requirement(r))
538-
for section in setup_requires:
539-
python_version = section
540-
not_python = not is_python(section)
541-
542-
# This is for cleaning up :extras: formatted markers
543-
# by adding them to the results of the resolver
544-
# since any such extra would have been returned as a result anyway
545-
for value in setup_requires[section]:
546-
547-
# This is a marker.
548-
if is_python(section):
549-
python_version = value[1:-1]
550-
else:
551-
not_python = True
552-
553-
if ":" not in value and not_python:
554-
try:
555-
requirement_str = "{0}{1}".format(value, python_version).replace(
556-
":", ";"
557-
)
558-
requirements.add(
559-
format_requirement(
560-
make_install_requirement(requirement_str).ireq
561-
)
562-
)
563-
# Anything could go wrong here -- can't be too careful.
564-
except Exception:
565-
pass
566-
488+
setup_info = SetupInfo.from_ireq(dep)
489+
results = setup_info.get_info()
490+
setup_requires.update(results["setup_requires"])
491+
requirements = set(results["requires"].values())
492+
else:
493+
results = pip_shims.shims.resolve(dep)
494+
requirements = [v for v in results.values() if v.name != dep.name]
495+
requirements = set([format_requirement(r) for r in requirements])
567496
if not dep.editable and is_pinned_requirement(dep) and requirements is not None:
568497
DEPENDENCY_CACHE[dep] = list(requirements)
569498
return requirements
@@ -694,10 +623,10 @@ def get_grouped_dependencies(constraints):
694623
# then we take the loose match (which _is_ flexible) and start moving backwards in
695624
# versions by popping them off of a stack and checking for the conflicting package
696625
for _, ireqs in full_groupby(constraints, key=key_from_ireq):
697-
ireqs = list(ireqs)
698-
editable_ireq = first(ireqs, key=lambda ireq: ireq.editable)
626+
ireqs = sorted(ireqs, key=lambda ireq: ireq.editable)
627+
editable_ireq = next(iter(ireq for ireq in ireqs if ireq.editable), None)
699628
if editable_ireq:
700-
yield editable_ireq # ignore all the other specs: the editable one is the one that counts
629+
yield editable_ireq # only the editable match mattters, ignore all others
701630
continue
702631
ireqs = iter(ireqs)
703632
# deepcopy the accumulator so as to not modify the self.our_constraints invariant

0 commit comments

Comments
 (0)