diff --git a/pipenv/core.py b/pipenv/core.py index d207ea4b9a..9981c9be94 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -742,6 +742,8 @@ def batch_install(deps_list, procs, failed_deps_queue, os.environ["PIP_USER"] = vistir.compat.fs_str("0") if "PYTHONHOME" in os.environ: del os.environ["PYTHONHOME"] + if "GIT_CONFIG" in os.environ and dep.is_vcs: + del os.environ["GIT_CONFIG"] c = pip_install( dep, @@ -1379,20 +1381,7 @@ def get_requirement_line( requirement.line_instance._wheel_kwargs.update({ "src_dir": src_dir }) - # if requirement.vcs and requirement.editable: - # repo = requirement.req.get_vcs_repo(src_dir=src_dir) - # requirement.line_instance.vcsrepo - # line = repo.url - # name = requirement.name - # line = "{0}+".format(requirement.vcs) if requirement.vcs else "" - # if requirement.extras: - # name = "{0}{1}".format(name, requirement.extras_as_pip) - # line = "{0}{1}#egg={2}".format( - # line, vistir.path.path_to_url(repo.checkout_directory), requirement.name - # ) - # if repo.subdirectory: - # line = "{0}&subdirectory={1}".format(line, repo.subdirectory) - # else: + requirement.line_instance.vcsrepo line = requirement.line_instance.line if requirement.line_instance.markers: line = '{0}; {1}'.format(line, requirement.line_instance.markers) @@ -1418,8 +1407,8 @@ def write_requirement_to_file( if not requirements_dir: requirements_dir = vistir.path.create_tracked_tempdir( prefix="pipenv", suffix="requirements") - line = get_requirement_line( - requirement, src_dir, include_hashes=include_hashes, format_for_file=True + line = requirement.line_instance.get_line( + with_prefix=True, with_hashes=include_hashes, with_markers=True, as_list=False ) f = vistir.compat.NamedTemporaryFile( @@ -1470,8 +1459,10 @@ def pip_install( elif not (requirement.is_vcs or requirement.editable or requirement.vcs): ignore_hashes = False line = None - if requirement.vcs and not requirement.line_instance.markers: - line = get_requirement_line(requirement, src_dir, include_hashes=not ignore_hashes, format_for_file=False) + if requirement.vcs: + line = requirement.line_instance.get_line( + with_prefix=True, with_hashes=False, with_markers=True, as_list=True + ) else: r = write_requirement_to_file( requirement, requirements_dir=requirements_dir, src_dir=src_dir, @@ -1541,6 +1532,7 @@ def pip_install( pip_command = cmd.cmdify() c = None c = delegator.run(pip_command, block=block, env=pip_config) + c.env = pip_config return c @@ -2109,14 +2101,6 @@ def do_install( sys.exit(1) if index_url: pkg_requirement.index = index_url - # deps = [] - # if pkg_requirement.is_vcs and PIPENV_RESOLVE_VCS: - # if not allow_global and ( - # pkg_requirement.line_instance and pkg_requirement.line_instance.wheel_kwargs - # ): - # pkg_requirement.line_instance._wheel_kwargs["src_dir"] = project.virtualenv_src_location - # pkg_setupinfo = pkg_requirement.line_instance.setup_info - # deps = pkg_setupinfo.requires no_deps = False sp.text = "Installing..." try: diff --git a/pipenv/utils.py b/pipenv/utils.py index cfda803a28..64333e2697 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -451,6 +451,7 @@ def get_deps_from_req(cls, req, resolver=None): from .vendor.requirementslib.models.utils import _requirement_to_str_lowercase_name from .vendor.requirementslib.models.requirements import Requirement from requirementslib.utils import is_installable_dir + # TODO: this is way too complex, refactor this constraints = set() # type: Set[str] locked_deps = dict() # type: Dict[str, Dict[str, Union[str, bool, List[str]]]] if (req.is_file_or_url or req.is_vcs) and not req.is_wheel: diff --git a/pipenv/vendor/requirementslib/__init__.py b/pipenv/vendor/requirementslib/__init__.py index 7f039c0751..70b604a82e 100644 --- a/pipenv/vendor/requirementslib/__init__.py +++ b/pipenv/vendor/requirementslib/__init__.py @@ -10,7 +10,7 @@ from .models.pipfile import Pipfile from .models.requirements import Requirement -__version__ = "1.5.1" +__version__ = "1.5.2.dev0" logger = logging.getLogger(__name__) diff --git a/pipenv/vendor/requirementslib/exceptions.py b/pipenv/vendor/requirementslib/exceptions.py index 17b884eb46..d11dbce9b4 100644 --- a/pipenv/vendor/requirementslib/exceptions.py +++ b/pipenv/vendor/requirementslib/exceptions.py @@ -1,19 +1,21 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function + import errno import os -import six import sys - +import six from vistir.compat import FileNotFoundError - if six.PY2: + class FileExistsError(OSError): def __init__(self, *args, **kwargs): self.errno = errno.EEXIST super(FileExistsError, self).__init__(*args, **kwargs) + + else: from six.moves.builtins import FileExistsError @@ -24,8 +26,15 @@ class RequirementError(Exception): class MissingParameter(Exception): def __init__(self, param): - Exception.__init__(self) - print("Missing parameter: %s" % param, file=sys.stderr, flush=True) + self.message = self.get_message(param) + super(MissingParameter, self).__init__(self.message) + + @classmethod + def get_message(cls, param): + return "Missing Parameter: %s" % param + + def show(self, param): + print(self.message, file=sys.stderr, flush=True) class FileCorruptException(OSError): @@ -35,58 +44,67 @@ def __init__(self, path, *args, **kwargs): if not backup_path and args: args = reversed(args) backup_path = args.pop() - if not isinstance(backup_path, six.string_types) or not os.path.exists(os.path.abspath(os.path.dirname(backup_path))): + if not isinstance(backup_path, six.string_types) or not os.path.exists( + os.path.abspath(os.path.dirname(backup_path)) + ): args.append(backup_path) backup_path = None if args: args = reversed(args) - self.path = path - self.backup_path = backup_path - self.show(self.path, self.backup_path) - OSError.__init__(self, path, *args, **kwargs) + self.message = self.get_message(path, backup_path=backup_path) + super(FileCorruptException, self).__init__(self.message) - @classmethod - def show(cls, path, backup_path=None): - print("ERROR: Failed to load file at %s" % path, file=sys.stderr, flush=True) + def get_message(self, path, backup_path=None): + message = "ERROR: Failed to load file at %s" % path if backup_path: msg = "it will be backed up to %s and removed" % backup_path else: - msg = "it will be removed and replaced." - print("The file is corrupt, %s" % msg, file=sys.stderr, flush=True) + msg = "it will be removed and replaced on the next lock." + message = "{0}\nYour lockfile is corrupt, {1}".format(message, msg) + return message + + def show(self): + print(self.message, file=sys.stderr, flush=True) class LockfileCorruptException(FileCorruptException): + def __init__(self, path, backup_path=None): + self.message = self.get_message(path, backup_path=backup_path) + super(LockfileCorruptException, self).__init__(self.message) - @classmethod - def show(cls, path, backup_path=None): - print("ERROR: Failed to load lockfile at %s" % path, file=sys.stderr, flush=True) + def get_message(self, path, backup_path=None): + message = "ERROR: Failed to load lockfile at %s" % path if backup_path: msg = "it will be backed up to %s and removed" % backup_path else: msg = "it will be removed and replaced on the next lock." - print("Your lockfile is corrupt, %s" % msg, file=sys.stderr, flush=True) + message = "{0}\nYour lockfile is corrupt, {1}".format(message, msg) + return message + + def show(self, path, backup_path=None): + print(self.message, file=sys.stderr, flush=True) class PipfileCorruptException(FileCorruptException): + def __init__(self, path, backup_path=None): + self.message = self.get_message(path, backup_path=backup_path) + super(PipfileCorruptException, self).__init__(self.message) - @classmethod - def show(cls, path, backup_path=None): - print("ERROR: Failed to load Pipfile at %s" % path, file=sys.stderr, flush=True) + def get_message(self, path, backup_path=None): + message = "ERROR: Failed to load Pipfile at %s" % path if backup_path: msg = "it will be backed up to %s and removed" % backup_path else: msg = "it will be removed and replaced on the next lock." - print("Your Pipfile is corrupt, %s" % msg, file=sys.stderr, flush=True) + message = "{0}\nYour Pipfile is corrupt, {1}".format(message, msg) + return message + + def show(self, path, backup_path=None): + print(self.message, file=sys.stderr, flush=True) class PipfileNotFound(FileNotFoundError): def __init__(self, path, *args, **kwargs): self.errno = errno.ENOENT - self.path = path - self.show(path) - super(PipfileNotFound, self).__init__(*args, **kwargs) - - @classmethod - def show(cls, path): - print("ERROR: The file could not be found: %s" % path, file=sys.stderr, flush=True) - print("Aborting...", file=sys.stderr, flush=True) + self.filename = path + super(PipfileNotFound, self).__init__(self.filename) diff --git a/pipenv/vendor/requirementslib/models/dependencies.py b/pipenv/vendor/requirementslib/models/dependencies.py index 44f34edb48..82eaba5f3e 100644 --- a/pipenv/vendor/requirementslib/models/dependencies.py +++ b/pipenv/vendor/requirementslib/models/dependencies.py @@ -9,34 +9,56 @@ import attr import packaging.markers import packaging.version +import pip_shims.shims import requests - from first import first from packaging.utils import canonicalize_name - -import pip_shims.shims -from vistir.compat import JSONDecodeError, fs_str, ResourceWarning +from vistir.compat import JSONDecodeError, fs_str from vistir.contextmanagers import cd, temp_environ from vistir.misc import partialclass from vistir.path import create_tracked_tempdir -from ..environment import MYPY_RUNNING -from ..utils import prepare_pip_source_args, _ensure_dir from .cache import CACHE_DIR, DependencyCache from .utils import ( - clean_requires_python, fix_requires_python_marker, format_requirement, - full_groupby, is_pinned_requirement, key_from_ireq, - make_install_requirement, name_from_req, version_from_ireq + clean_requires_python, + fix_requires_python_marker, + format_requirement, + full_groupby, + is_pinned_requirement, + key_from_ireq, + make_install_requirement, + name_from_req, + version_from_ireq, ) - +from ..environment import MYPY_RUNNING +from ..utils import _ensure_dir, prepare_pip_source_args if MYPY_RUNNING: - from typing import Any, Dict, List, Generator, Optional, Union, Tuple, TypeVar, Text, Set, AnyStr - from pip_shims.shims import InstallRequirement, InstallationCandidate, PackageFinder, Command + from typing import ( + Any, + Dict, + List, + Generator, + Optional, + Union, + Tuple, + TypeVar, + Text, + Set, + ) + from pip_shims.shims import ( + InstallRequirement, + InstallationCandidate, + PackageFinder, + Command, + ) from packaging.requirements import Requirement as PackagingRequirement + TRequirement = TypeVar("TRequirement") - RequirementType = TypeVar('RequirementType', covariant=True, bound=PackagingRequirement) - MarkerType = TypeVar('MarkerType', covariant=True, bound=Marker) + RequirementType = TypeVar( + "RequirementType", covariant=True, bound=PackagingRequirement + ) + MarkerType = TypeVar("MarkerType", covariant=True, bound=Marker) STRING_TYPE = Union[str, bytes, Text] S = TypeVar("S", bytes, str, Text) @@ -67,7 +89,6 @@ def find_all_matches(finder, ireq, pre=False): :rtype: list[:class:`~pip._internal.index.InstallationCandidate`] """ - candidates = clean_requires_python(finder.find_all_candidates(ireq.name)) versions = {candidate.version for candidate in candidates} allowed_versions = _get_filtered_versions(ireq, versions, pre) @@ -158,10 +179,14 @@ def compatible_abstract_dep(self, other): elif len(other.candidates) == 1 and first(other.candidates).editable: return other new_specifiers = self.specifiers & other.specifiers - markers = set(self.markers,) if self.markers else set() + markers = set(self.markers) if self.markers else set() if other.markers: markers.add(other.markers) - new_markers = packaging.markers.Marker(" or ".join(str(m) for m in sorted(markers))) + new_markers = None + if markers: + new_markers = packaging.markers.Marker( + " or ".join(str(m) for m in sorted(markers)) + ) new_ireq = copy.deepcopy(self.requirement.ireq) new_ireq.req.specifier = new_specifiers new_ireq.req.marker = new_markers @@ -187,7 +212,7 @@ def compatible_abstract_dep(self, other): requirement=new_requirement, parent=self.parent, dep_dict=dep_dict, - finder=self.finder + finder=self.finder, ) def get_deps(self, candidate): @@ -204,7 +229,7 @@ def get_deps(self, candidate): from .requirements import Requirement req = Requirement.from_line(key) - req.merge_markers(self.markers) + req = req.merge_markers(self.markers) self.dep_dict[key] = req.get_abstract_dependencies() return self.dep_dict[key] @@ -230,13 +255,18 @@ def from_requirement(cls, requirement, parent=None): if not is_pinned and not requirement.editable: for r in requirement.find_all_matches(finder=finder): req = make_install_requirement( - name, r.version, extras=extras, markers=markers, constraint=is_constraint, + name, + r.version, + extras=extras, + markers=markers, + constraint=is_constraint, ) req.req.link = r.location req.parent = parent candidates.append(req) candidates = sorted( - set(candidates), key=lambda k: packaging.version.parse(version_from_ireq(k)), + set(candidates), + key=lambda k: packaging.version.parse(version_from_ireq(k)), ) else: candidates = [requirement.ireq] @@ -279,9 +309,7 @@ def get_abstract_dependencies(reqs, sources=None, parent=None): for req in reqs: if isinstance(req, pip_shims.shims.InstallRequirement): - requirement = Requirement.from_line( - "{0}{1}".format(req.name, req.specifier) - ) + requirement = Requirement.from_line("{0}{1}".format(req.name, req.specifier)) if req.link: requirement.req.link = req.link requirement.markers = req.markers @@ -311,27 +339,26 @@ def get_dependencies(ireq, sources=None, parent=None): :rtype: set(str) """ if not isinstance(ireq, pip_shims.shims.InstallRequirement): - name = getattr( - ireq, "project_name", - getattr(ireq, "project", ireq.name), - ) + name = getattr(ireq, "project_name", getattr(ireq, "project", ireq.name)) version = getattr(ireq, "version", None) if not version: ireq = pip_shims.shims.InstallRequirement.from_line("{0}".format(name)) else: - ireq = pip_shims.shims.InstallRequirement.from_line("{0}=={1}".format(name, version)) + ireq = pip_shims.shims.InstallRequirement.from_line( + "{0}=={1}".format(name, version) + ) pip_options = get_pip_options(sources=sources) getters = [ get_dependencies_from_cache, get_dependencies_from_wheel_cache, get_dependencies_from_json, - functools.partial(get_dependencies_from_index, pip_options=pip_options) + functools.partial(get_dependencies_from_index, pip_options=pip_options), ] for getter in getters: deps = getter(ireq) if deps is not None: return deps - raise RuntimeError('failed to get dependencies for {}'.format(ireq)) + raise RuntimeError("failed to get dependencies for {}".format(ireq)) def get_dependencies_from_wheel_cache(ireq): @@ -389,7 +416,7 @@ def gen(ireq): finally: session.close() requires_dist = info.get("requires_dist", info.get("requires")) - if not requires_dist: # The API can return None for this. + if not requires_dist: # The API can return None for this. return for requires in requires_dist: i = pip_shims.shims.InstallRequirement.from_line(requires) @@ -430,9 +457,9 @@ def get_dependencies_from_cache(ireq): dep_ireq = pip_shims.shims.InstallRequirement.from_line(line) name = canonicalize_name(dep_ireq.name) if _marker_contains_extra(dep_ireq): - broken = True # The "extra =" marker breaks everything. + broken = True # The "extra =" marker breaks everything. elif name == canonicalize_name(ireq.name): - broken = True # A package cannot depend on itself. + broken = True # A package cannot depend on itself. if broken: break except Exception: @@ -446,7 +473,7 @@ def get_dependencies_from_cache(ireq): def is_python(section): - return section.startswith('[') and ':' in section + return section.startswith("[") and ":" in section def get_dependencies_from_index(dep, sources=None, pip_options=None, wheel_cache=None): @@ -468,12 +495,15 @@ def get_dependencies_from_index(dep, sources=None, pip_options=None, wheel_cache reqset.add_requirement(dep) requirements = None setup_requires = {} - with temp_environ(), start_resolver(finder=finder, wheel_cache=wheel_cache) as resolver: - os.environ['PIP_EXISTS_ACTION'] = 'i' + with temp_environ(), start_resolver( + finder=finder, wheel_cache=wheel_cache + ) as resolver: + os.environ["PIP_EXISTS_ACTION"] = "i" dist = None if dep.editable and not dep.prepared and not dep.req: with cd(dep.setup_py_dir): from setuptools.dist import distutils + try: dist = distutils.core.run_setup(dep.setup_py) except (ImportError, TypeError, AttributeError): @@ -504,7 +534,7 @@ def get_dependencies_from_index(dep, sources=None, pip_options=None, wheel_cache add_marker = fix_requires_python_marker(requires_python) reqset.remove(dep) if dep.req.marker: - dep.req.marker._markers.extend(['and',].extend(add_marker._markers)) + dep.req.marker._markers.extend(["and"].extend(add_marker._markers)) else: dep.req.marker = add_marker reqset.add(dep) @@ -512,7 +542,7 @@ def get_dependencies_from_index(dep, sources=None, pip_options=None, wheel_cache for r in results: if requires_python: if r.req.marker: - r.req.marker._markers.extend(['and',].extend(add_marker._markers)) + r.req.marker._markers.extend(["and"].extend(add_marker._markers)) else: r.req.marker = add_marker requirements.add(format_requirement(r)) @@ -531,10 +561,16 @@ def get_dependencies_from_index(dep, sources=None, pip_options=None, wheel_cache else: not_python = True - if ':' not in value and not_python: + if ":" not in value and not_python: try: - requirement_str = "{0}{1}".format(value, python_version).replace(":", ";") - requirements.add(format_requirement(make_install_requirement(requirement_str).ireq)) + requirement_str = "{0}{1}".format(value, python_version).replace( + ":", ";" + ) + requirements.add( + format_requirement( + make_install_requirement(requirement_str).ireq + ) + ) # Anything could go wrong here -- can't be too careful. except Exception: pass @@ -559,9 +595,7 @@ def get_pip_options(args=[], sources=None, pip_command=None): if not pip_command: pip_command = get_pip_command() if not sources: - sources = [ - {"url": "https://pypi.org/simple", "name": "pypi", "verify_ssl": True} - ] + sources = [{"url": "https://pypi.org/simple", "name": "pypi", "verify_ssl": True}] _ensure_dir(CACHE_DIR) pip_args = args pip_args = prepare_pip_source_args(sources, pip_args) @@ -587,9 +621,7 @@ def get_finder(sources=None, pip_command=None, pip_options=None): if not pip_command: pip_command = get_pip_command() if not sources: - sources = [ - {"url": "https://pypi.org/simple", "name": "pypi", "verify_ssl": True} - ] + sources = [{"url": "https://pypi.org/simple", "name": "pypi", "verify_ssl": True}] if not pip_options: pip_options = get_pip_options(sources=sources, pip_command=pip_command) session = pip_command._build_session(pip_options) @@ -652,7 +684,9 @@ def start_resolver(finder=None, wheel_cache=None): use_user_site=False, ) try: - if packaging.version.parse(pip_shims.shims.pip_version) >= packaging.version.parse('18'): + if packaging.version.parse( + pip_shims.shims.pip_version + ) >= packaging.version.parse("18"): with pip_shims.shims.RequirementTracker() as req_tracker: preparer = preparer(req_tracker=req_tracker) yield resolver(preparer=preparer) diff --git a/pipenv/vendor/requirementslib/models/markers.py b/pipenv/vendor/requirementslib/models/markers.py index 5e66511422..fc85fbdd51 100644 --- a/pipenv/vendor/requirementslib/models/markers.py +++ b/pipenv/vendor/requirementslib/models/markers.py @@ -7,7 +7,7 @@ import packaging.version import six from packaging.markers import InvalidMarker, Marker -from packaging.specifiers import InvalidSpecifier, Specifier, SpecifierSet +from packaging.specifiers import Specifier, SpecifierSet from vistir.compat import Mapping, Set, lru_cache from vistir.misc import dedup @@ -19,18 +19,7 @@ if MYPY_RUNNING: - from typing import ( - Optional, - List, - Type, - Any, - Tuple, - Union, - Set, - AnyStr, - Text, - Iterator, - ) + from typing import Optional, List, Type, Any, Tuple, Union, AnyStr, Text, Iterator STRING_TYPE = Union[str, bytes, Text] @@ -277,8 +266,8 @@ def cleanup_pyspecs(specs, joiner="or"): }, # leave these the same no matter what operator we use ("!=", "==", "~=", "==="): { - "or": lambda x: get_sorted_version_string(x), - "and": lambda x: get_sorted_version_string(x), + "or": get_sorted_version_string, + "and": get_sorted_version_string, }, } op_translations = { diff --git a/pipenv/vendor/requirementslib/models/pipfile.py b/pipenv/vendor/requirementslib/models/pipfile.py index 3f7b20c2e6..e55ad741a6 100644 --- a/pipenv/vendor/requirementslib/models/pipfile.py +++ b/pipenv/vendor/requirementslib/models/pipfile.py @@ -242,7 +242,11 @@ def __getattr__(self, k, *args, **kwargs): @property def requires_python(self): # type: () -> bool - return self._pipfile.requires.requires_python + return getattr( + self._pipfile.requires, + "python_version", + getattr(self._pipfile.requires, "python_full_version", None), + ) @property def allow_prereleases(self): diff --git a/pipenv/vendor/requirementslib/models/project.py b/pipenv/vendor/requirementslib/models/project.py index 28afcf0ba5..7c1b0e8100 100644 --- a/pipenv/vendor/requirementslib/models/project.py +++ b/pipenv/vendor/requirementslib/models/project.py @@ -1,6 +1,6 @@ # -*- coding=utf-8 -*- -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import collections import io @@ -13,14 +13,10 @@ import plette.models import six import tomlkit +from vistir.compat import FileNotFoundError - -SectionDifference = collections.namedtuple("SectionDifference", [ - "inthis", "inthat", -]) -FileDifference = collections.namedtuple("FileDifference", [ - "default", "develop", -]) +SectionDifference = collections.namedtuple("SectionDifference", ["inthis", "inthat"]) +FileDifference = collections.namedtuple("FileDifference", ["default", "develop"]) def _are_pipfile_entries_equal(a, b): @@ -52,12 +48,15 @@ def preferred_newlines(f): class ProjectFile(object): """A file in the Pipfile project. """ + location = attr.ib() line_ending = attr.ib() model = attr.ib() @classmethod def read(cls, location, model_cls, invalid_ok=False): + if not os.path.exists(location) and not invalid_ok: + raise FileNotFoundError(location) try: with io.open(location, encoding="utf-8") as f: model = model_cls.load(f) @@ -89,14 +88,9 @@ class Project(object): def __attrs_post_init__(self): self.root = root = os.path.abspath(self.root) - self._p = ProjectFile.read( - os.path.join(root, "Pipfile"), - plette.Pipfile, - ) + self._p = ProjectFile.read(os.path.join(root, "Pipfile"), plette.Pipfile) self._l = ProjectFile.read( - os.path.join(root, "Pipfile.lock"), - plette.Lockfile, - invalid_ok=True, + os.path.join(root, "Pipfile.lock"), plette.Lockfile, invalid_ok=True ) @property @@ -138,14 +132,17 @@ def contains_key_in_pipfile(self, key): self._get_pipfile_section(develop=True, insert=False), ] return any( - (packaging.utils.canonicalize_name(name) == - packaging.utils.canonicalize_name(key)) + ( + packaging.utils.canonicalize_name(name) + == packaging.utils.canonicalize_name(key) + ) for section in sections for name in section ) def add_line_to_pipfile(self, line, develop): from requirementslib import Requirement + requirement = Requirement.from_line(line) section = self._get_pipfile_section(develop=develop) key = requirement.normalized_name @@ -164,13 +161,9 @@ def remove_keys_from_pipfile(self, keys, default, develop): keys = {packaging.utils.canonicalize_name(key) for key in keys} sections = [] if default: - sections.append(self._get_pipfile_section( - develop=False, insert=False, - )) + sections.append(self._get_pipfile_section(develop=False, insert=False)) if develop: - sections.append(self._get_pipfile_section( - develop=True, insert=False, - )) + sections.append(self._get_pipfile_section(develop=True, insert=False)) for section in sections: removals = set() for name in section: diff --git a/pipenv/vendor/requirementslib/models/requirements.py b/pipenv/vendor/requirementslib/models/requirements.py index 559ab424d1..40a49d48c0 100644 --- a/pipenv/vendor/requirementslib/models/requirements.py +++ b/pipenv/vendor/requirementslib/models/requirements.py @@ -190,7 +190,10 @@ def __hash__(self): tuple(self.extras), tuple(self.hashes), self.vcs, - self.ireq, + self.uri, + self.path, + self.name, + self._requirement, ) ) @@ -208,6 +211,58 @@ def __repr__(self): except Exception: return "".format(self.__dict__.values()) + def __str__(self): + # type: () -> str + if self.markers: + return "{0}; {1}".format(self.get_line(), self.markers) + return self.get_line() + + def get_line( + self, with_prefix=False, with_markers=False, with_hashes=True, as_list=False + ): + # type: (bool, bool, bool, bool) -> Union[STRING_TYPE, List[STRING_TYPE]] + line = self.line + extras_str = extras_to_string(self.extras) + with_hashes = False if self.editable or self.is_vcs else with_hashes + hash_list = ["--hash={0}".format(h) for h in self.hashes] + if self.is_named: + line = self.name_and_specifier + elif self.is_direct_url: + line = self.link.url + elif extras_str: + if self.is_vcs: + line = self.link.url + if "git+file:/" in line and "git+file:///" not in line: + line = line.replace("git+file:/", "git+file:///") + elif extras_str not in line: + line = "{0}{1}".format(line, extras_str) + # XXX: For using markers on vcs or url requirements, they can be used + # as normal (i.e. no space between the requirement and the semicolon) + # and no additional quoting as long as they are not editable requirements + # HOWEVER, for editable requirements, the requirement+marker must be quoted + # We do this here for the line-formatted versions, but leave it up to the + # `Script.parse()` functionality in pipenv, for instance, to handle that + # in a cross-platform manner for the `as_list` approach since that is how + # we anticipate this will be used if passing directly to the command line + # for pip. + if with_markers and self.markers: + line = "{0}; {1}".format(line, self.markers) + if with_prefix and self.editable and not as_list: + line = '"{0}"'.format(line) + if as_list: + result_list = [] + if with_prefix and self.editable: + result_list.append("-e") + result_list.append(line) + if with_hashes: + result_list.extend(self.hashes) + return result_list + if with_prefix and self.editable: + line = "-e {0}".format(line) + if with_hashes and hash_list: + line = "{0} {1}".format(line, " ".join(hash_list)) + return line + @property def name_and_specifier(self): name_str, spec_str = "", "" @@ -240,22 +295,7 @@ def split_hashes(cls, line): @property def line_with_prefix(self): # type: () -> STRING_TYPE - line = self.line - if self.is_named: - return self.name_and_specifier - extras_str = extras_to_string(self.extras) - if self.is_direct_url: - line = self.link.url - elif extras_str: - if self.is_vcs: - line = self.link.url - if "git+file:/" in line and "git+file:///" not in line: - line = line.replace("git+file:/", "git+file:///") - elif extras_str not in line: - line = "{0}{1}".format(line, extras_str) - if self.editable: - return "-e {0}".format(line) - return line + return self.get_line(with_prefix=True, with_hashes=False) @property def line_for_ireq(self): @@ -2570,37 +2610,33 @@ def update_name_from_path(self, path): if self.req._setup_info and self.req._setup_info.name is None: self.req._setup_info.name = name + def get_line_instance(self): + # type: () -> Line + line_parts = [] + if self.req: + line_parts.append(self.req.line_part) + if not self.is_vcs and not self.vcs and self.extras_as_pip: + line_parts.append(self.extras_as_pip) + if self._specifiers and not (self.is_file_or_url or self.is_vcs): + line_parts.append(self._specifiers) + if self.markers: + line_parts.append("; {0}".format(self.markers)) + if self.hashes_as_pip: + line_parts.append(self.hashes_as_pip) + line = "".join(line_parts) + return Line(line) + @property def line_instance(self): # type: () -> Optional[Line] if self._line_instance is None: - if self.req is not None and self.req._parsed_line is not None: - self._line_instance = self.req._parsed_line - else: - include_extras = True - include_specifiers = True - if self.is_vcs: - include_extras = False - if self.is_file_or_url or self.is_vcs or not self._specifiers: - include_specifiers = False - line_part = "" # type: STRING_TYPE - if self.req and self.req.line_part: - line_part = "{0!s}".format(self.req.line_part) - parts = [] # type: List[STRING_TYPE] - parts = [ - line_part, - self.extras_as_pip if include_extras else "", - self._specifiers if include_specifiers and self._specifiers else "", - self.markers_as_pip, - ] - line = "".join(parts) - self._line_instance = Line(line) + self.line_instance = self.get_line_instance() return self._line_instance @line_instance.setter def line_instance(self, line_instance): # type: (Line) -> None - if self.req and not self.req._parsed_line: + if self.req: self.req._parsed_line = line_instance self._line_instance = line_instance @@ -2834,29 +2870,14 @@ def as_line( in the requirement line. """ - include_specifiers = True if self.specifiers else False - if self.is_vcs: - include_extras = False - if self.is_file_or_url or self.is_vcs: - include_specifiers = False - parts = [ - self.req.line_part, - self.extras_as_pip if include_extras else "", - self.specifiers if include_specifiers else "", - self.markers_as_pip if include_markers else "", - ] - if as_list: - # This is used for passing to a subprocess call - parts = ["".join(parts)] - if include_hashes: - hashes = self.get_hashes_as_pip(as_list=as_list) - if as_list: - parts.extend(hashes) - else: - parts.append(hashes) - - is_local = self.is_file_or_url and self.req and self.req.is_local - if sources and self.requirement and not (is_local or self.vcs): + assert self.line_instance is not None + parts = self.line_instance.get_line( + with_prefix=True, + with_hashes=include_hashes, + with_markers=include_markers, + as_list=as_list, + ) + if sources and self.requirement and not (self.line_instance.is_local or self.vcs): from ..utils import prepare_pip_source_args if self.index: @@ -2866,11 +2887,8 @@ def as_line( parts.extend(sources) else: index_string = " ".join(source_list) - parts.extend([" ", index_string]) - if as_list: - return parts - line = "".join(parts) - return line + parts = "{0} {1}".format(parts, index_string) + return parts def get_markers(self): # type: () -> Marker @@ -3093,6 +3111,8 @@ def run_requires(self, sources=None, finder=None): def merge_markers(self, markers): # type: (Union[AnyStr, Marker]) -> None + if not markers: + return self if not isinstance(markers, Marker): markers = Marker(markers) _markers = [] # type: List[Marker] diff --git a/pipenv/vendor/requirementslib/models/utils.py b/pipenv/vendor/requirementslib/models/utils.py index fd5567a8ab..1c1c320d93 100644 --- a/pipenv/vendor/requirementslib/models/utils.py +++ b/pipenv/vendor/requirementslib/models/utils.py @@ -829,7 +829,9 @@ def name_from_req(req): return req.name -def make_install_requirement(name, version, extras, markers, constraint=False): +def make_install_requirement( + name, version=None, extras=None, markers=None, constraint=False +): """ Generates an :class:`~pip._internal.req.req_install.InstallRequirement`. @@ -853,19 +855,16 @@ def make_install_requirement(name, version, extras, markers, constraint=False): from pip_shims.shims import install_req_from_line extras_string = "" + requirement_string = "{0}".format(name) if extras: # Sort extras for stability extras_string = "[{}]".format(",".join(sorted(extras))) - - if not markers: - return install_req_from_line( - str("{}{}=={}".format(name, extras_string, version)), constraint=constraint - ) - else: - return install_req_from_line( - str("{}{}=={}; {}".format(name, extras_string, version, str(markers))), - constraint=constraint, - ) + requirement_string = "{0}{1}".format(requirement_string, extras_string) + if version: + requirement_string = "{0}=={1}".format(requirement_string, str(version)) + if markers: + requirement_string = "{0}; {1}".format(requirement_string, str(markers)) + return install_req_from_line(requirement_string, constraint=constraint) def version_from_ireq(ireq): @@ -986,7 +985,6 @@ def read_source(path, encoding="utf-8"): return fp.read() - SETUPTOOLS_SHIM = ( "import setuptools, tokenize;__file__=%r;" "f=getattr(tokenize, 'open', open)(__file__);" diff --git a/pipenv/vendor/requirementslib/utils.py b/pipenv/vendor/requirementslib/utils.py index 3769dbac1f..503a13d071 100644 --- a/pipenv/vendor/requirementslib/utils.py +++ b/pipenv/vendor/requirementslib/utils.py @@ -121,7 +121,7 @@ def strip_ssh_from_git_uri(uri): def add_ssh_scheme_to_git_uri(uri): # type: (S) -> S - """Cleans VCS uris from pipenv.patched.notpip format""" + """Cleans VCS uris from pip format""" if isinstance(uri, six.string_types): # Add scheme for parsing purposes, this is also what pip does if uri.startswith("git+") and "://" not in uri: