Skip to content

Commit dad1c20

Browse files
authored
Merge pull request #4248 from pypa/bugfix/4231
2 parents 9ab617a + c7425e7 commit dad1c20

File tree

21 files changed

+576
-19
lines changed

21 files changed

+576
-19
lines changed

news/4231.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed a bug which caused pipenv to prefer source distributions over wheels from ``PyPI`` during the dependency resolution phase.
2+
Fixed an issue which prevented proper build isolation using ``pep517`` based builders during dependency resolution.

pipenv/environments.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,67 @@
1414
# HACK: avoid resolver.py uses the wrong byte code files.
1515
# I hope I can remove this one day.
1616
os.environ["PYTHONDONTWRITEBYTECODE"] = fs_str("1")
17+
_false_values = ("0", "false", "no", "off")
18+
_true_values = ("1", "true", "yes", "on")
19+
20+
21+
def env_to_bool(val):
22+
"""
23+
Convert **val** to boolean, returning True if truthy or False if falsey
24+
25+
:param Any val: The value to convert
26+
:return: False if Falsey, True if truthy
27+
:rtype: bool
28+
"""
29+
if isinstance(val, bool):
30+
return val
31+
if val.lower() in _false_values:
32+
return False
33+
if val.lower() in _true_values:
34+
return True
35+
raise ValueError("Value is not a valid boolean-like: {0}".format(val))
1736

1837

1938
def _is_env_truthy(name):
2039
"""An environment variable is truthy if it exists and isn't one of (0, false, no, off)
2140
"""
2241
if name not in os.environ:
2342
return False
24-
return os.environ.get(name).lower() not in ("0", "false", "no", "off")
43+
return os.environ.get(name).lower() not in _false_values
44+
45+
46+
def get_from_env(arg, prefix="PIPENV", check_for_negation=True):
47+
"""
48+
Check the environment for a variable, returning its truthy or stringified value
49+
50+
For example, setting ``PIPENV_NO_RESOLVE_VCS=1`` would mean that
51+
``get_from_env("RESOLVE_VCS", prefix="PIPENV")`` would return ``False``.
52+
53+
:param str arg: The name of the variable to look for
54+
:param str prefix: The prefix to attach to the variable, defaults to "PIPENV"
55+
:param bool check_for_negation: Whether to check for ``<PREFIX>_NO_<arg>``, defaults
56+
to True
57+
:return: The value from the environment if available
58+
:rtype: Optional[Union[str, bool]]
59+
"""
60+
negative_lookup = "NO_{0}".format(arg)
61+
positive_lookup = arg
62+
if prefix:
63+
positive_lookup = "{0}_{1}".format(prefix, arg)
64+
negative_lookup = "{0}_{1}".format(prefix, negative_lookup)
65+
if positive_lookup in os.environ:
66+
value = os.environ[positive_lookup]
67+
try:
68+
return env_to_bool(value)
69+
except ValueError:
70+
return value
71+
if check_for_negation and negative_lookup in os.environ:
72+
value = os.environ[negative_lookup]
73+
try:
74+
return not env_to_bool(value)
75+
except ValueError:
76+
return value
77+
return None
2578

2679

2780
PIPENV_IS_CI = bool("CI" in os.environ or "TF_BUILD" in os.environ)

pipenv/patched/notpip/__main__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
# Resulting path is the name of the wheel itself
1212
# Add that to sys.path so we can import pipenv.patched.notpip
1313
path = os.path.dirname(os.path.dirname(__file__))
14+
pipenv = os.path.dirname(os.path.dirname(path))
1415
sys.path.insert(0, path)
16+
sys.path.insert(0, pipenv)
1517

1618
from pipenv.patched.notpip._internal.cli.main import main as _main # isort:skip # noqa
1719

pipenv/patched/notpip/_internal/index/package_finder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ def _sort_key(self, candidate, ignore_compatibility=True):
535535
)
536536
if self._prefer_binary:
537537
binary_preference = 1
538-
tags = self.valid_tags if not ignore_compatibility else None
538+
tags = valid_tags
539539
try:
540540
pri = -(wheel.support_index_min(tags=tags))
541541
except TypeError:

pipenv/patched/piptools/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def simplify_markers(ireq):
7676
def clean_requires_python(candidates):
7777
"""Get a cleaned list of all the candidates with valid specifiers in the `requires_python` attributes."""
7878
all_candidates = []
79-
py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', '.'.join(map(str, sys.version_info[:3]))))
79+
py_version = parse_version(os.environ.get('PIPENV_REQUESTED_PYTHON_VERSION', '.'.join(map(str, sys.version_info[:3]))))
8080
for c in candidates:
8181
if getattr(c, "requires_python", None):
8282
# Old specifications had people setting this to single digits

pipenv/resolver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ def resolve(packages, pre, project, sources, clear, system, requirements_dir=Non
771771

772772

773773
def _main(pre, clear, verbose, system, write, requirements_dir, packages, parse_only=False):
774-
os.environ["PIP_PYTHON_VERSION"] = ".".join([str(s) for s in sys.version_info[:3]])
774+
os.environ["PIPENV_REQUESTED_PYTHON_VERSION"] = ".".join([str(s) for s in sys.version_info[:3]])
775775
os.environ["PIP_PYTHON_PATH"] = str(sys.executable)
776776
if parse_only:
777777
parse_packages(

pipenv/utils.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -236,14 +236,14 @@ def __init__(self, python_version, python_path):
236236
def __enter__(self):
237237
# Only inject when the value is valid
238238
if self.python_version:
239-
os.environ["PIP_PYTHON_VERSION"] = str(self.python_version)
239+
os.environ["PIPENV_REQUESTED_PYTHON_VERSION"] = str(self.python_version)
240240
if self.python_path:
241241
os.environ["PIP_PYTHON_PATH"] = str(self.python_path)
242242

243243
def __exit__(self, *args):
244244
# Restore original Python version information.
245245
try:
246-
del os.environ["PIP_PYTHON_VERSION"]
246+
del os.environ["PIPENV_REQUESTED_PYTHON_VERSION"]
247247
except KeyError:
248248
pass
249249

@@ -682,25 +682,21 @@ def pip_command(self):
682682
self._pip_command = self._get_pip_command()
683683
return self._pip_command
684684

685-
def prepare_pip_args(self, use_pep517=True, build_isolation=True):
685+
def prepare_pip_args(self, use_pep517=False, build_isolation=True):
686686
pip_args = []
687687
if self.sources:
688688
pip_args = prepare_pip_source_args(self.sources, pip_args)
689-
if not use_pep517:
689+
if use_pep517 is False:
690690
pip_args.append("--no-use-pep517")
691-
if not build_isolation:
691+
if build_isolation is False:
692692
pip_args.append("--no-build-isolation")
693693
pip_args.extend(["--cache-dir", environments.PIPENV_CACHE_DIR])
694694
return pip_args
695695

696696
@property
697697
def pip_args(self):
698-
use_pep517 = False if (
699-
os.environ.get("PIP_NO_USE_PEP517", None) is not None
700-
) else (True if os.environ.get("PIP_USE_PEP517", None) is not None else None)
701-
build_isolation = False if (
702-
os.environ.get("PIP_NO_BUILD_ISOLATION", None) is not None
703-
) else (True if os.environ.get("PIP_BUILD_ISOLATION", None) is not None else None)
698+
use_pep517 = environments.get_from_env("USE_PEP517", prefix="PIP")
699+
build_isolation = environments.get_from_env("BUILD_ISOLATION", prefix="PIP")
704700
if self._pip_args is None:
705701
self._pip_args = self.prepare_pip_args(
706702
use_pep517=use_pep517, build_isolation=build_isolation
@@ -790,6 +786,7 @@ def get_resolver(self, clear=False, pre=False):
790786
self._resolver = PiptoolsResolver(
791787
constraints=self.parsed_constraints, repository=self.repository,
792788
cache=DependencyCache(environments.PIPENV_CACHE_DIR), clear_caches=clear,
789+
# TODO: allow users to toggle the 'allow unsafe' flag to resolve setuptools?
793790
prereleases=pre, allow_unsafe=False
794791
)
795792

tasks/vendoring/patches/patched/pip20.patch

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ index 02a187c8..f917e645 100644
114114
modifying_pip=modifying_pip
115115
)
116116
diff --git a/pipenv/patched/pip/_internal/index/package_finder.py b/pipenv/patched/pip/_internal/index/package_finder.py
117-
index a74d78db..11128f4d 100644
117+
index a74d78db..7c9dc1be 100644
118118
--- a/pipenv/patched/pip/_internal/index/package_finder.py
119119
+++ b/pipenv/patched/pip/_internal/index/package_finder.py
120120
@@ -121,6 +121,7 @@ class LinkEvaluator(object):
@@ -201,7 +201,7 @@ index a74d78db..11128f4d 100644
201201
if self._prefer_binary:
202202
binary_preference = 1
203203
- pri = -(wheel.support_index_min(valid_tags))
204-
+ tags = self.valid_tags if not ignore_compatibility else None
204+
+ tags = valid_tags
205205
+ try:
206206
+ pri = -(wheel.support_index_min(tags=tags))
207207
+ except TypeError:
@@ -589,3 +589,17 @@ index 65e41bc7..9eabf28e 100644
589589

590590

591591
class AdjacentTempDirectory(TempDirectory):
592+
diff --git a/pipenv/patched/pip/__main__.py b/pipenv/patched/pip/__main__.py
593+
index 56f669fa..3c216189 100644
594+
--- a/pipenv/patched/pip/__main__.py
595+
+++ b/pipenv/patched/pip/__main__.py
596+
@@ -11,7 +11,9 @@ if __package__ == '':
597+
# Resulting path is the name of the wheel itself
598+
# Add that to sys.path so we can import pip
599+
path = os.path.dirname(os.path.dirname(__file__))
600+
+ pipenv = os.path.dirname(os.path.dirname(path))
601+
sys.path.insert(0, path)
602+
+ sys.path.insert(0, pipenv)
603+
604+
from pip._internal.cli.main import main as _main # isort:skip # noqa
605+

tasks/vendoring/patches/patched/piptools.patch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ index 7733447..e6f232f 100644
745745
+def clean_requires_python(candidates):
746746
+ """Get a cleaned list of all the candidates with valid specifiers in the `requires_python` attributes."""
747747
+ all_candidates = []
748-
+ py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', '.'.join(map(str, sys.version_info[:3]))))
748+
+ py_version = parse_version(os.environ.get('PIPENV_REQUESTED_PYTHON_VERSION', '.'.join(map(str, sys.version_info[:3]))))
749749
+ for c in candidates:
750750
+ if getattr(c, "requires_python", None):
751751
+ # Old specifications had people setting this to single digits
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[build-system]
2+
requires = ["setuptools >= 40.6.0", "setuptools-scm", "cython"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[tool.black]
6+
line-length = 90
7+
target_version = ['py27', 'py35', 'py36', 'py37', 'py38']
8+
include = '\.pyi?$'
9+
exclude = '''
10+
/(
11+
\.eggs
12+
| \.git
13+
| \.hg
14+
| \.mypy_cache
15+
| \.tox
16+
| \.pyre_configuration
17+
| \.venv
18+
| _build
19+
| buck-out
20+
| build
21+
| dist
22+
)
23+
'''
24+
25+
[tool.towncrier]
26+
package = 'cython-import-package'
27+
package_dir = 'src'
28+
filename = 'CHANGELOG.rst'
29+
directory = 'news/'
30+
title_format = '{version} ({project_date})'
31+
issue_format = '`#{issue} <https://github.com/sarugaku/cython_import_package/issues/{issue}>`_'
32+
template = 'tasks/CHANGELOG.rst.jinja2'
33+
34+
[[tool.towncrier.type]]
35+
directory = 'feature'
36+
name = 'Features'
37+
showcontent = true
38+
39+
[[tool.towncrier.type]]
40+
directory = 'bugfix'
41+
name = 'Bug Fixes'
42+
showcontent = true
43+
44+
[[tool.towncrier.type]]
45+
directory = 'trivial'
46+
name = 'Trivial Changes'
47+
showcontent = false
48+
49+
[[tool.towncrier.type]]
50+
directory = 'removal'
51+
name = 'Removals and Deprecations'
52+
showcontent = true

0 commit comments

Comments
 (0)