diff --git a/pipenv/vendor/requirementslib/__init__.py b/pipenv/vendor/requirementslib/__init__.py index 72ce5b71..f3393c11 100644 --- a/pipenv/vendor/requirementslib/__init__.py +++ b/pipenv/vendor/requirementslib/__init__.py @@ -1,5 +1,5 @@ # -*- coding=utf-8 -*- -__version__ = "1.0.1" +__version__ = "1.0.5.dev0" from .exceptions import RequirementError diff --git a/pipenv/vendor/requirementslib/models/lockfile.py b/pipenv/vendor/requirementslib/models/lockfile.py index b79e1947..c04ae021 100644 --- a/pipenv/vendor/requirementslib/models/lockfile.py +++ b/pipenv/vendor/requirementslib/models/lockfile.py @@ -9,8 +9,8 @@ from .._compat import Path, FileNotFoundError @attr.s class Lockfile(object): - dev_requirements = attr.ib(default=list) - requirements = attr.ib(default=list) + dev_requirements = attr.ib(default=attr.Factory(list)) + requirements = attr.ib(default=attr.Factory(list)) path = attr.ib(default=None, validator=optional_instance_of(Path)) pipfile_hash = attr.ib(default=None) @@ -31,7 +31,8 @@ class Lockfile(object): if not lockfile_path.exists(): raise FileNotFoundError("No such lockfile: %s" % lockfile_path) - lockfile = json.loads(lockfile_path.read_text(encoding="utf-8")) + with lockfile_path.open(encoding="utf-8") as f: + lockfile = json.loads(f.read()) for k in lockfile["develop"].keys(): dev_requirements.append(Requirement.from_pipfile(k, lockfile["develop"][k])) for k in lockfile["default"].keys(): diff --git a/pipenv/vendor/requirementslib/models/requirements.py b/pipenv/vendor/requirementslib/models/requirements.py index 71e228f0..16d99863 100644 --- a/pipenv/vendor/requirementslib/models/requirements.py +++ b/pipenv/vendor/requirementslib/models/requirements.py @@ -324,7 +324,7 @@ class FileRequirement(BaseRequirement): vcs_type, prefer, relpath, path, uri, link = cls.get_link_from_line(line) setup_path = Path(path) / "setup.py" if path else None arg_dict = { - "path": relpath or path, + "path": relpath if relpath else path, "uri": unquote(link.url_without_fragment), "link": link, "editable": editable, @@ -347,6 +347,11 @@ class FileRequirement(BaseRequirement): uri = pipfile.get("uri") fil = pipfile.get("file") path = pipfile.get("path") + if path: + if isinstance(path, Path) and not path.is_absolute(): + path = get_converted_relative_path(path.as_posix()) + elif not os.path.isabs(path): + path = get_converted_relative_path(path) if path and uri: raise ValueError("do not specify both 'path' and 'uri'") if path and fil: @@ -387,7 +392,7 @@ class FileRequirement(BaseRequirement): ): seed = unquote(self.link.url_without_fragment) or self.uri else: - seed = self.formatted_path or self.link.url or self.uri + seed = self.formatted_path or unquote(self.link.url_without_fragment) or self.uri # add egg fragments to remote artifacts (valid urls only) if not self._has_hashed_name and self.is_remote_artifact: seed += "#egg={0}".format(self.name) @@ -789,9 +794,7 @@ class Requirement(object): @property def constraint_line(self): - if self.is_named or self.is_vcs: - return self.as_line() - return self.req.req.line + return self.as_line() def as_pipfile(self): good_keys = ( diff --git a/pipenv/vendor/requirementslib/models/utils.py b/pipenv/vendor/requirementslib/models/utils.py index 13b2b368..b7daf890 100644 --- a/pipenv/vendor/requirementslib/models/utils.py +++ b/pipenv/vendor/requirementslib/models/utils.py @@ -84,7 +84,7 @@ def strip_ssh_from_git_uri(uri): def add_ssh_scheme_to_git_uri(uri): - """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: diff --git a/pipenv/vendor/requirementslib/utils.py b/pipenv/vendor/requirementslib/utils.py index e25d2e85..02511c93 100644 --- a/pipenv/vendor/requirementslib/utils.py +++ b/pipenv/vendor/requirementslib/utils.py @@ -69,8 +69,11 @@ def get_converted_relative_path(path, relative_to=os.curdir): path = start.joinpath(".", path).relative_to(start) # Normalize these to use forward slashes even on windows if os.name == "nt": - return os.altsep.join([".", path.as_posix()]) - return os.sep.join([".", path.as_posix()]) + relpath = os.altsep.join([".", path.as_posix()]) + relpath = os.sep.join([".", path.as_posix()]) + if relpath in ['./.', '.\\.']: + relpath = '.' + return relpath def multi_split(s, split): diff --git a/pipenv/vendor/vendor.txt b/pipenv/vendor/vendor.txt index eafcec98..aa1e3126 100644 --- a/pipenv/vendor/vendor.txt +++ b/pipenv/vendor/vendor.txt @@ -27,7 +27,7 @@ requests==2.19.1 idna==2.7 urllib3==1.23 certifi==2018.4.16 -requirementslib==1.0.1 +requirementslib==1.0.4 attrs==18.1.0 distlib==0.2.7 packaging==17.1 diff --git a/tests/integration/test_install_basic.py b/tests/integration/test_install_basic.py index a6798aa3..047c9d78 100644 --- a/tests/integration/test_install_basic.py +++ b/tests/integration/test_install_basic.py @@ -2,7 +2,13 @@ import contextlib import os from pipenv.utils import temp_environ +from pipenv._compat import TemporaryDirectory from pipenv.vendor import delegator +from pipenv.project import Project +try: + from pathlib import Path +except ImportError: + from pipenv.vendor.pathlib2 import Path import pytest @@ -267,6 +273,31 @@ def test_requirements_to_pipfile(PipenvInstance, pypi): assert 'pysocks' in p.lockfile['default'] +@pytest.mark.install +@pytest.mark.requirements +def test_skip_requirements_when_pipfile(PipenvInstance, pypi): + + with PipenvInstance(chdir=True, pypi=pypi) as p: + with open('requirements.txt', 'w') as f: + f.write('requests==2.18.1\n') + c = p.pipenv('install six') + assert c.return_code == 0 + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +six = "*" +tablib = "<0.12" + """.strip() + f.write(contents) + c = p.pipenv('install') + assert 'tablib' in p.pipfile['packages'] + assert 'tablib' in p.lockfile['default'] + assert 'six' in p.pipfile['packages'] + assert 'six' in p.lockfile['default'] + assert 'requests' not in p.pipfile['packages'] + assert 'requests' not in p.lockfile['default'] + + @pytest.mark.cli @pytest.mark.clean def test_clean_on_empty_venv(PipenvInstance, pypi): @@ -277,6 +308,9 @@ def test_clean_on_empty_venv(PipenvInstance, pypi): @pytest.mark.install def test_install_does_not_extrapolate_environ(PipenvInstance, pypi): + """This test is deisgned to make sure that pipenv ignores requirements.txt files + for projects that already exist (already have a Pipfile) as well as for times when a + package name is passed in to the install command.""" with temp_environ(), PipenvInstance(pypi=pypi, chdir=True) as p: os.environ['PYPI_URL'] = pypi.url @@ -309,3 +343,42 @@ def test_editable_no_args(PipenvInstance): c = p.pipenv('install -e') assert c.return_code != 0 assert 'Please provide path to editable package' in c.err + + +@pytest.mark.install +@pytest.mark.virtualenv +def test_install_venv_project_directory(PipenvInstance, pypi): + """Test pew's project functionality during virtualenv creation. Since .venv + virtualenvs are not created with pew, we need to swap to a workon_home based + virtualenv for this test""" + with PipenvInstance(pypi=pypi, chdir=True) as p: + with temp_environ(), TemporaryDirectory(prefix='pipenv-', suffix='temp_workon_home') as workon_home: + os.environ['WORKON_HOME'] = workon_home.name + if 'PIPENV_VENV_IN_PROJECT' in os.environ: + del os.environ['PIPENV_VENV_IN_PROJECT'] + c = p.pipenv('install six') + assert c.return_code == 0 + project = Project() + assert Path(project.virtualenv_location).joinpath('.project').exists() + + +@pytest.mark.deploy +@pytest.mark.system +def test_system_and_deploy_work(PipenvInstance, pypi): + with PipenvInstance(chdir=True, pypi=pypi) as p: + c = p.pipenv('install six requests') + assert c.return_code == 0 + c = p.pipenv('--rm') + assert c.return_code == 0 + c = delegator.run('virtualenv .venv') + assert c.return_code == 0 + c = p.pipenv('install --system --deploy') + assert c.return_code == 0 + c = p.pipenv('--rm') + assert c.return_code == 0 + Path(p.pipfile_path).write_text(u""" +[packages] +requests + """.strip()) + c = p.pipenv('install --system') + assert c.return_code == 0 diff --git a/tests/integration/test_install_twists.py b/tests/integration/test_install_twists.py index e12b2ab6..0a48bafb 100644 --- a/tests/integration/test_install_twists.py +++ b/tests/integration/test_install_twists.py @@ -1,6 +1,6 @@ import os import shutil - +from pipenv.project import Project try: import pathlib except ImportError: @@ -16,36 +16,48 @@ from flaky import flaky @pytest.mark.extras @pytest.mark.install @pytest.mark.local -@pytest.mark.skip(reason="I'm not mocking this.") -def test_local_extras_install(PipenvInstance, pypi): - with PipenvInstance(pypi=pypi) as p: +@pytest.mark.parametrize('line, pipfile', [ + ['-e .[dev]', {'testpipenv': {'path': '.', 'editable': True, 'extras': ['dev']}}] +]) +def test_local_extras_install(PipenvInstance, pypi, line, pipfile): + """Test -e .[extras] installs... note that the extras themselves + are currently not landing in the lockfile for reasons that are unclear. + """ + with PipenvInstance(pypi=pypi, chdir=True) as p: + project = Project() setup_py = os.path.join(p.path, 'setup.py') with open(setup_py, 'w') as fh: contents = """ from setuptools import setup, find_packages - setup( -name='test_pipenv', +name='testpipenv', version='0.1', description='Pipenv Test Package', author='Pipenv Test', author_email='test@pipenv.package', -license='PIPENV', +license='MIT', packages=find_packages(), -install_requires=['tablib'], -extras_require={'dev': ['flake8', 'pylint']}, +install_requires=[], +extras_require={'dev': ['six']}, zip_safe=False ) """.strip() fh.write(contents) - c = p.pipenv('install .[dev]') + project.write_toml({'packages': pipfile, 'dev-packages': {}}) + c = p.pipenv('install') assert c.return_code == 0 - key = [k for k in p.pipfile['packages'].keys()][0] - dep = p.pipfile['packages'][key] - assert dep['path'] == '.' - assert dep['extras'] == ['dev'] - assert key in p.lockfile['default'] - assert 'dev' in p.lockfile['default'][key]['extras'] + assert 'testpipenv' in p.lockfile['default'] + assert p.lockfile['default']['testpipenv']['extras'] == ['dev'] + assert 'six' in p.lockfile['default'] + c = p.pipenv('--rm') + assert c.return_code == 0 + project.write_toml({'packages': {}, 'dev-packages': {}}) + c = p.pipenv('install {0}'.format(line)) + assert c.return_code == 0 + assert 'testpipenv' in p.pipfile['packages'] + assert p.pipfile['packages']['testpipenv']['path'] == '.' + assert p.pipfile['packages']['testpipenv']['extras'] == ['dev'] + assert 'six' in p.lockfile['default'] @pytest.mark.e @@ -230,3 +242,25 @@ def test_install_local_file_collision(PipenvInstance, pypi): assert target_package in p.pipfile['packages'] assert p.pipfile['packages'][target_package] == '*' assert target_package in p.lockfile['default'] + + +@pytest.mark.url +@pytest.mark.install +def test_install_local_uri_special_character(PipenvInstance, testsroot): + file_name = 'six-1.11.0+mkl-py2.py3-none-any.whl' + source_path = os.path.abspath(os.path.join(testsroot, 'test_artifacts', file_name)) + with PipenvInstance() as p: + artifact_dir = 'artifacts' + artifact_path = os.path.join(p.path, artifact_dir) + mkdir_p(artifact_path) + shutil.copy(source_path, os.path.join(artifact_path, file_name)) + with open(p.pipfile_path, 'w') as f: + contents = """ +# Pre comment +[packages] +six = {{path = "./artifacts/{}"}} + """.format(file_name) + f.write(contents.strip()) + c = p.pipenv('install') + assert c.return_code == 0 + assert 'six' in p.lockfile['default'] diff --git a/tests/integration/test_lock.py b/tests/integration/test_lock.py index ce57e544..0dfd22bb 100644 --- a/tests/integration/test_lock.py +++ b/tests/integration/test_lock.py @@ -307,12 +307,11 @@ requests = "==2.14.0" """.strip().format(url=pypi.url) f.write(contents) - os.environ['MY_ENV_VAR'] = 'simple' - c = p.pipenv('lock') - assert c.return_code == 0 - assert 'requests' in p.lockfile['default'] - - del os.environ['MY_ENV_VAR'] + with temp_environ(): + os.environ['MY_ENV_VAR'] = 'simple' + c = p.pipenv('lock') + assert c.return_code == 0 + assert 'requests' in p.lockfile['default'] with open(p.pipfile_path, 'w') as f: contents = """ @@ -328,3 +327,21 @@ requests = "==2.14.0" assert c.return_code == 0 assert 'requests' in p.lockfile['default'] + +@pytest.mark.lock +@pytest.mark.vcs +@pytest.mark.needs_internet +def lock_editable_vcs_without_install(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi, chdir=True) as p: + with open(p.pipfile_path, 'w') as f: + f.write(""" +[packages] +requests = {git = "https://github.com/requests/requests.git", ref = "master", editable = true} + """.strip()) + c = p.pipenv('lock') + assert c.return_code == 0 + assert 'requests' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'chardet' in p.lockfile['default'] + c = p.pipenv('install') + assert c.return_code == 0 diff --git a/tests/integration/test_pipenv.py b/tests/integration/test_pipenv.py index 30a955bd..82117704 100644 --- a/tests/integration/test_pipenv.py +++ b/tests/integration/test_pipenv.py @@ -5,8 +5,7 @@ XXX: Try our best to reduce tests in this file. from pipenv.core import activate_virtualenv from pipenv.project import Project - - +from pipenv.vendor import delegator import pytest @@ -83,3 +82,13 @@ def test_update_locks(PipenvInstance, pypi): assert c.return_code == 0 lines = c.out.splitlines() assert 'requests==2.19.1' in [l.strip() for l in lines] + + +@pytest.mark.project +@pytest.mark.proper_names +def test_proper_names_unamanged_virtualenv(PipenvInstance, pypi): + with PipenvInstance(chdir=True, pypi=pypi) as p: + c = delegator.run('python -m virtualenv .venv') + assert c.return_code == 0 + project = Project() + assert project.proper_names == [] diff --git a/tests/test_artifacts/six-1.11.0+mkl-py2.py3-none-any.whl b/tests/test_artifacts/six-1.11.0+mkl-py2.py3-none-any.whl new file mode 100644 index 00000000..59960239 Binary files /dev/null and b/tests/test_artifacts/six-1.11.0+mkl-py2.py3-none-any.whl differ