Commit 2c37180f by Sébastien Eustace

Merge branch 'master' into develop

parents 9f79b899 cb649141
...@@ -15,7 +15,7 @@ install: ...@@ -15,7 +15,7 @@ install:
# Upgrade to the latest version of pip to avoid it displaying warnings # Upgrade to the latest version of pip to avoid it displaying warnings
# about it being out of date. # about it being out of date.
- "python -m pip install --disable-pip-version-check --user --upgrade pip" - "python -m pip install --disable-pip-version-check --user pip==18.1"
# Installing Poetry # Installing Poetry
- "python get-poetry.py --preview -y" - "python get-poetry.py --preview -y"
......
...@@ -11,7 +11,7 @@ cache: ...@@ -11,7 +11,7 @@ cache:
- "$HOME/.cache/pre-commit" - "$HOME/.cache/pre-commit"
install: install:
- pip install pip -U - pip install pip==18.1
- python get-poetry.py --preview -y - python get-poetry.py --preview -y
- source $HOME/.poetry/env - source $HOME/.poetry/env
- poetry install -v - poetry install -v
......
...@@ -24,6 +24,30 @@ ...@@ -24,6 +24,30 @@
- Fixed transitive extra dependencies being removed when updating a specific dependency. - Fixed transitive extra dependencies being removed when updating a specific dependency.
## [0.12.15] - 2019-05-03
### Fixed
- Fixed an `AttributeError` in the editable builder.
- Fixed resolution of packages with only Python 3 wheels and sdist when resolving for legacy repositories.
- Fixed non-sha256 hashes retrieval for legacy repositories.
## [0.12.14] - 2019-04-26
### Fixed
- Fixed root package installation for pure Python packages.
## [0.12.13] - 2019-04-26
### Fixed
- Fixed root package installation with `pip>=19.0`.
- Fixed packages not being removed after using the `remove` command.
## [0.12.12] - 2019-04-11 ## [0.12.12] - 2019-04-11
### Fixed ### Fixed
...@@ -661,7 +685,10 @@ Initial release ...@@ -661,7 +685,10 @@ Initial release
[Unreleased]: https://github.com/sdispater/poetry/compare/0.12.12...develop [Unreleased]: https://github.com/sdispater/poetry/compare/0.12.15...develop
[0.12.15]: https://github.com/sdispater/poetry/releases/tag/0.12.15
[0.12.14]: https://github.com/sdispater/poetry/releases/tag/0.12.14
[0.12.13]: https://github.com/sdispater/poetry/releases/tag/0.12.13
[0.12.12]: https://github.com/sdispater/poetry/releases/tag/0.12.12 [0.12.12]: https://github.com/sdispater/poetry/releases/tag/0.12.12
[0.12.11]: https://github.com/sdispater/poetry/releases/tag/0.12.11 [0.12.11]: https://github.com/sdispater/poetry/releases/tag/0.12.11
[0.12.10]: https://github.com/sdispater/poetry/releases/tag/0.12.10 [0.12.10]: https://github.com/sdispater/poetry/releases/tag/0.12.10
......
...@@ -29,10 +29,8 @@ exist it will look for <comment>pyproject.toml</> and do the same. ...@@ -29,10 +29,8 @@ exist it will look for <comment>pyproject.toml</> and do the same.
def handle(self): def handle(self):
from clikit.io import NullIO from clikit.io import NullIO
from poetry.installation import Installer from poetry.installation import Installer
from poetry.masonry.builders import SdistBuilder from poetry.masonry.builders import EditableBuilder
from poetry.masonry.utils.module import ModuleOrPackageNotFound from poetry.masonry.utils.module import ModuleOrPackageNotFound
from poetry.utils._compat import decode
from poetry.utils.env import NullEnv
installer = Installer( installer = Installer(
self.io, self.env, self.poetry.package, self.poetry.locker, self.poetry.pool self.io, self.env, self.poetry.package, self.poetry.locker, self.poetry.pool
...@@ -60,7 +58,7 @@ exist it will look for <comment>pyproject.toml</> and do the same. ...@@ -60,7 +58,7 @@ exist it will look for <comment>pyproject.toml</> and do the same.
return 0 return 0
try: try:
builder = SdistBuilder(self.poetry, NullEnv(), NullIO()) builder = EditableBuilder(self.poetry, self._env, NullIO())
except ModuleOrPackageNotFound: except ModuleOrPackageNotFound:
# This is likely due to the fact that the project is an application # This is likely due to the fact that the project is an application
# not following the structure expected by Poetry # not following the structure expected by Poetry
...@@ -76,17 +74,6 @@ exist it will look for <comment>pyproject.toml</> and do the same. ...@@ -76,17 +74,6 @@ exist it will look for <comment>pyproject.toml</> and do the same.
if self.option("dry-run"): if self.option("dry-run"):
return 0 return 0
setup = self.poetry.file.parent / "setup.py" builder.build()
has_setup = setup.exists()
if has_setup: return 0
self.line("<warning>A setup.py file already exists. Using it.</warning>")
else:
with setup.open("w", encoding="utf-8") as f:
f.write(decode(builder.build_setup()))
try:
self.env.run("pip", "install", "-e", str(setup.parent), "--no-deps")
finally:
if not has_setup:
os.remove(str(setup))
...@@ -211,7 +211,6 @@ class Installer: ...@@ -211,7 +211,6 @@ class Installer:
# Making a new repo containing the packages # Making a new repo containing the packages
# newly resolved and the ones from the current lock file # newly resolved and the ones from the current lock file
locked_repository = self._locker.locked_repository(True)
repo = Repository() repo = Repository()
for package in local_repo.packages + locked_repository.packages: for package in local_repo.packages + locked_repository.packages:
if not repo.has_package(package): if not repo.has_package(package):
......
...@@ -116,7 +116,11 @@ class PipInstaller(BaseInstaller): ...@@ -116,7 +116,11 @@ class PipInstaller(BaseInstaller):
if formatted and not package.source_type: if formatted and not package.source_type:
req = "{}=={}".format(package.name, package.version) req = "{}=={}".format(package.name, package.version)
for h in package.hashes: for h in package.hashes:
req += " --hash sha256:{}".format(h) hash_type = "sha256"
if ":" in h:
hash_type, h = h.split(":")
req += " --hash {}:{}".format(hash_type, h)
req += "\n" req += "\n"
......
from .complete import CompleteBuilder from .complete import CompleteBuilder
from .editable import EditableBuilder
from .sdist import SdistBuilder from .sdist import SdistBuilder
from .wheel import WheelBuilder from .wheel import WheelBuilder
...@@ -37,7 +37,7 @@ class Builder(object): ...@@ -37,7 +37,7 @@ class Builder(object):
AVAILABLE_PYTHONS = {"2", "2.7", "3", "3.4", "3.5", "3.6", "3.7"} AVAILABLE_PYTHONS = {"2", "2.7", "3", "3.4", "3.5", "3.6", "3.7"}
def __init__(self, poetry, env, io): def __init__(self, poetry, env, io): # type: (Poetry, Env, IO) -> None
self._poetry = poetry self._poetry = poetry
self._env = env self._env = env
self._io = io self._io = io
......
from __future__ import unicode_literals
import os
import shutil
from collections import defaultdict
from poetry.semver.version import Version
from poetry.utils._compat import decode
from .builder import Builder
from .sdist import SdistBuilder
class EditableBuilder(Builder):
def build(self):
return self._setup_build()
def _setup_build(self):
builder = SdistBuilder(self._poetry, self._env, self._io)
setup = self._path / "setup.py"
has_setup = setup.exists()
if has_setup:
self._io.write_line(
"<warning>A setup.py file already exists. Using it.</warning>"
)
else:
with setup.open("w", encoding="utf-8") as f:
f.write(decode(builder.build_setup()))
try:
if self._env.pip_version < Version(19, 0):
self._env.run("python", "-m", "pip", "install", "-e", str(self._path))
else:
# Temporarily rename pyproject.toml
shutil.move(
str(self._poetry.file), str(self._poetry.file.with_suffix(".tmp"))
)
try:
self._env.run(
"python", "-m", "pip", "install", "-e", str(self._path)
)
finally:
shutil.move(
str(self._poetry.file.with_suffix(".tmp")),
str(self._poetry.file),
)
finally:
if not has_setup:
os.remove(str(setup))
def _build_egg_info(self):
egg_info = self._path / "{}.egg-info".format(
self._package.name.replace("-", "_")
)
egg_info.mkdir(exist_ok=True)
with egg_info.joinpath("PKG-INFO").open("w", encoding="utf-8") as f:
f.write(decode(self.get_metadata_content()))
with egg_info.joinpath("entry_points.txt").open("w", encoding="utf-8") as f:
entry_points = self.convert_entry_points()
for group_name in sorted(entry_points):
f.write("[{}]\n".format(group_name))
for ep in sorted(entry_points[group_name]):
f.write(ep.replace(" ", "") + "\n")
f.write("\n")
with egg_info.joinpath("requires.txt").open("w", encoding="utf-8") as f:
f.write(self._generate_requires())
def _build_egg_link(self):
egg_link = self._env.site_packages / "{}.egg-link".format(self._package.name)
with egg_link.open("w", encoding="utf-8") as f:
f.write(str(self._poetry.file.parent.resolve()) + "\n")
f.write(".")
def _add_easy_install_entry(self):
easy_install_pth = self._env.site_packages / "easy-install.pth"
path = str(self._poetry.file.parent.resolve())
content = ""
if easy_install_pth.exists():
with easy_install_pth.open(encoding="utf-8") as f:
content = f.read()
if path in content:
return
content += "{}\n".format(path)
with easy_install_pth.open("w", encoding="utf-8") as f:
f.write(content)
def _generate_requires(self):
extras = defaultdict(list)
requires = ""
for dep in sorted(self._package.requires, key=lambda d: d.name):
marker = dep.marker
if marker.is_any():
requires += "{}\n".format(dep.base_pep_508_name)
continue
extras[str(marker)].append(dep.base_pep_508_name)
if extras:
requires += "\n"
for marker, deps in sorted(extras.items()):
requires += "[:{}]\n".format(marker)
for dep in deps:
requires += dep + "\n"
requires += "\n"
return requires
...@@ -341,6 +341,7 @@ class LegacyRepository(PyPiRepository): ...@@ -341,6 +341,7 @@ class LegacyRepository(PyPiRepository):
"requires_dist": [], "requires_dist": [],
"requires_python": None, "requires_python": None,
"digests": [], "digests": [],
"_cache_version": str(self.CACHE_VERSION),
} }
links = list(page.links_for_version(Version.parse(version))) links = list(page.links_for_version(Version.parse(version)))
...@@ -363,6 +364,8 @@ class LegacyRepository(PyPiRepository): ...@@ -363,6 +364,8 @@ class LegacyRepository(PyPiRepository):
hash = link.hash hash = link.hash
if link.hash_name == "sha256": if link.hash_name == "sha256":
hashes.append(hash) hashes.append(hash)
else:
hashes.append(link.hash_name + ":" + hash)
data["digests"] = hashes data["digests"] = hashes
......
...@@ -420,6 +420,13 @@ class PyPiRepository(Repository): ...@@ -420,6 +420,13 @@ class PyPiRepository(Repository):
if info: if info:
return info return info
# Prefer non platform specific wheels
if universal_python3_wheel:
return self._get_info_from_wheel(universal_python3_wheel)
if universal_python2_wheel:
return self._get_info_from_wheel(universal_python2_wheel)
if platform_specific_wheels and "sdist" not in urls: if platform_specific_wheels and "sdist" not in urls:
# Pick the first wheel available and hope for the best # Pick the first wheel available and hope for the best
return self._get_info_from_wheel(platform_specific_wheels[0]) return self._get_info_from_wheel(platform_specific_wheels[0])
......
...@@ -24,7 +24,7 @@ from clikit.api.io import IO ...@@ -24,7 +24,7 @@ from clikit.api.io import IO
from poetry.config import Config from poetry.config import Config
from poetry.locations import CACHE_DIR from poetry.locations import CACHE_DIR
from poetry.semver import Version from poetry.semver.version import Version
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils._compat import decode from poetry.utils._compat import decode
from poetry.utils._compat import encode from poetry.utils._compat import encode
...@@ -516,7 +516,13 @@ class EnvManager(object): ...@@ -516,7 +516,13 @@ class EnvManager(object):
try: try:
from venv import EnvBuilder from venv import EnvBuilder
builder = EnvBuilder(with_pip=True) # use the same defaults as python -m venv
if os.name == "nt":
use_symlinks = False
else:
use_symlinks = True
builder = EnvBuilder(with_pip=True, symlinks=use_symlinks)
build = builder.create build = builder.create
except ImportError: except ImportError:
# We fallback on virtualenv for Python 2.7 # We fallback on virtualenv for Python 2.7
...@@ -563,6 +569,7 @@ class Env(object): ...@@ -563,6 +569,7 @@ class Env(object):
self._base = base or path self._base = base or path
self._marker_env = None self._marker_env = None
self._pip_version = None
@property @property
def path(self): # type: () -> Path def path(self): # type: () -> Path
...@@ -609,6 +616,25 @@ class Env(object): ...@@ -609,6 +616,25 @@ class Env(object):
def os(self): # type: () -> str def os(self): # type: () -> str
return os.name return os.name
@property
def pip_version(self):
if self._pip_version is None:
self._pip_version = self.get_pip_version()
return self._pip_version
@property
def site_packages(self): # type: () -> Path
if self._is_windows:
return self._path / "Lib" / "site-packages"
return (
self._path
/ "lib"
/ "python{}.{}".format(*self.version_info[:2])
/ "site-packages"
)
@classmethod @classmethod
def get_base_prefix(cls): # type: () -> Path def get_base_prefix(cls): # type: () -> Path
if hasattr(sys, "real_prefix"): if hasattr(sys, "real_prefix"):
...@@ -631,6 +657,9 @@ class Env(object): ...@@ -631,6 +657,9 @@ class Env(object):
def config_var(self, var): # type: (str) -> Any def config_var(self, var): # type: (str) -> Any
raise NotImplementedError() raise NotImplementedError()
def get_pip_version(self): # type: () -> Version
raise NotImplementedError()
def is_valid_for_marker(self, marker): # type: (BaseMarker) -> bool def is_valid_for_marker(self, marker): # type: (BaseMarker) -> bool
return marker.validate(self.marker_env) return marker.validate(self.marker_env)
...@@ -750,6 +779,11 @@ class SystemEnv(Env): ...@@ -750,6 +779,11 @@ class SystemEnv(Env):
return return
def get_pip_version(self): # type: () -> Version
from pip import __version__
return Version.parse(__version__)
def is_venv(self): # type: () -> bool def is_venv(self): # type: () -> bool
return self._path != self._base return self._path != self._base
...@@ -800,6 +834,14 @@ class VirtualEnv(Env): ...@@ -800,6 +834,14 @@ class VirtualEnv(Env):
return value return value
def get_pip_version(self): # type: () -> Version
output = self.run("python", "-m", "pip", "--version").strip()
m = re.match("pip (.+?)(?: from .+)?$", output)
if not m:
return Version.parse("0.0")
return Version.parse(m.group(1))
def is_venv(self): # type: () -> bool def is_venv(self): # type: () -> bool
return True return True
...@@ -878,6 +920,7 @@ class MockEnv(NullEnv): ...@@ -878,6 +920,7 @@ class MockEnv(NullEnv):
platform="darwin", platform="darwin",
os_name="posix", os_name="posix",
is_venv=False, is_venv=False,
pip_version="19.1",
**kwargs **kwargs
): ):
super(MockEnv, self).__init__(**kwargs) super(MockEnv, self).__init__(**kwargs)
...@@ -887,6 +930,7 @@ class MockEnv(NullEnv): ...@@ -887,6 +930,7 @@ class MockEnv(NullEnv):
self._platform = platform self._platform = platform
self._os_name = os_name self._os_name = os_name
self._is_venv = is_venv self._is_venv = is_venv
self._pip_version = Version.parse(pip_version)
@property @property
def version_info(self): # type: () -> Tuple[int] def version_info(self): # type: () -> Tuple[int]
...@@ -904,5 +948,9 @@ class MockEnv(NullEnv): ...@@ -904,5 +948,9 @@ class MockEnv(NullEnv):
def os(self): # type: () -> str def os(self): # type: () -> str
return self._os_name return self._os_name
@property
def pip_version(self):
return self._pip_version
def is_venv(self): # type: () -> bool def is_venv(self): # type: () -> bool
return self._is_venv return self._is_venv
...@@ -76,6 +76,7 @@ class Locker(BaseLocker): ...@@ -76,6 +76,7 @@ class Locker(BaseLocker):
package["python-versions"] = python_versions package["python-versions"] = python_versions
self._written_data = data self._written_data = data
self._lock_data = data
@pytest.fixture() @pytest.fixture()
...@@ -331,7 +332,7 @@ def test_run_whitelist_add(installer, locker, repo, package): ...@@ -331,7 +332,7 @@ def test_run_whitelist_add(installer, locker, repo, package):
assert locker.written_data == expected assert locker.written_data == expected
def test_run_whitelist_remove(installer, locker, repo, package): def test_run_whitelist_remove(installer, locker, repo, package, installed):
locker.locked(True) locker.locked(True)
locker.mock_lock_data( locker.mock_lock_data(
{ {
...@@ -367,6 +368,7 @@ def test_run_whitelist_remove(installer, locker, repo, package): ...@@ -367,6 +368,7 @@ def test_run_whitelist_remove(installer, locker, repo, package):
package_b = get_package("B", "1.1") package_b = get_package("B", "1.1")
repo.add_package(package_a) repo.add_package(package_a)
repo.add_package(package_b) repo.add_package(package_b)
installed.add_package(package_b)
package.add_dependency("A", "~1.0") package.add_dependency("A", "~1.0")
...@@ -377,6 +379,9 @@ def test_run_whitelist_remove(installer, locker, repo, package): ...@@ -377,6 +379,9 @@ def test_run_whitelist_remove(installer, locker, repo, package):
expected = fixture("remove") expected = fixture("remove")
assert locker.written_data == expected assert locker.written_data == expected
assert len(installer.installer.installs) == 1
assert len(installer.installer.updates) == 0
assert len(installer.installer.removals) == 1
def test_add_with_sub_dependencies(installer, locker, repo, package): def test_add_with_sub_dependencies(installer, locker, repo, package):
......
from poetry.installation.pip_installer import PipInstaller
from poetry.io.null_io import NullIO
from poetry.packages.package import Package
from poetry.utils.env import NullEnv
def test_requirement():
installer = PipInstaller(NullEnv(), NullIO())
package = Package("ipython", "7.5.0")
package.hashes = [
"md5:dbdc53e3918f28fa335a173432402a00",
"e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26",
]
result = installer.requirement(package, formatted=True)
expected = (
"ipython==7.5.0 "
"--hash md5:dbdc53e3918f28fa335a173432402a00 "
"--hash sha256:e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26"
"\n"
)
assert expected == result
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from clikit.io import NullIO
from poetry.masonry.builders import EditableBuilder
from poetry.poetry import Poetry
from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
fixtures_dir = Path(__file__).parent / "fixtures"
def test_build_should_delegate_to_pip_for_non_pure_python_packages(tmp_dir, mocker):
move = mocker.patch("shutil.move")
tmp_dir = Path(tmp_dir)
env = MockEnv(path=tmp_dir, pip_version="18.1", execute=False)
env.site_packages.mkdir(parents=True)
module_path = fixtures_dir / "extended"
builder = EditableBuilder(Poetry.create(module_path), env, NullIO())
builder.build()
expected = [["python", "-m", "pip", "install", "-e", str(module_path)]]
assert expected == env.executed
assert 0 == move.call_count
def test_build_should_temporarily_remove_the_pyproject_file(tmp_dir, mocker):
move = mocker.patch("shutil.move")
tmp_dir = Path(tmp_dir)
env = MockEnv(path=tmp_dir, pip_version="19.1", execute=False)
env.site_packages.mkdir(parents=True)
module_path = fixtures_dir / "extended"
builder = EditableBuilder(Poetry.create(module_path), env, NullIO())
builder.build()
expected = [["python", "-m", "pip", "install", "-e", str(module_path)]]
assert expected == env.executed
assert 2 == move.call_count
expected_calls = [
mocker.call(
str(module_path / "pyproject.toml"), str(module_path / "pyproject.tmp")
),
mocker.call(
str(module_path / "pyproject.tmp"), str(module_path / "pyproject.toml")
),
]
assert expected_calls == move.call_args_list
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
<a href="https://files.pythonhosted.org/packages/52/19/aadde98d6bde1667d0bf431fb2d22451f880aaa373e0a241c7e7cb5815a0/ipython-5.7.0-py2-none-any.whl#sha256=707d1bbfc81e41e39ead1012af931bec6f80357b87e520af352e539cf5961dc0">ipython-5.7.0-py2-none-any.whl</a><br/> <a href="https://files.pythonhosted.org/packages/52/19/aadde98d6bde1667d0bf431fb2d22451f880aaa373e0a241c7e7cb5815a0/ipython-5.7.0-py2-none-any.whl#sha256=707d1bbfc81e41e39ead1012af931bec6f80357b87e520af352e539cf5961dc0">ipython-5.7.0-py2-none-any.whl</a><br/>
<a href="https://files.pythonhosted.org/packages/c7/b6/03e0b5b0972e6161d16c4cec8d41a20372bd0634f8cb4cc0c984b8a91db6/ipython-5.7.0-py3-none-any.whl#sha256=fc0464e68f9c65cd8c453474b4175432cc29ecb6c83775baedf6dbfcee9275ab">ipython-5.7.0-py3-none-any.whl</a><br/> <a href="https://files.pythonhosted.org/packages/c7/b6/03e0b5b0972e6161d16c4cec8d41a20372bd0634f8cb4cc0c984b8a91db6/ipython-5.7.0-py3-none-any.whl#sha256=fc0464e68f9c65cd8c453474b4175432cc29ecb6c83775baedf6dbfcee9275ab">ipython-5.7.0-py3-none-any.whl</a><br/>
<a href="https://files.pythonhosted.org/packages/3c/fd/559fead731a29eaa55cc235c8029807b2520976a937c30e9ee603f3bb566/ipython-5.7.0.tar.gz#sha256=8db43a7fb7619037c98626613ff08d03dda9d5d12c84814a4504c78c0da8323c">ipython-5.7.0.tar.gz</a><br/> <a href="https://files.pythonhosted.org/packages/3c/fd/559fead731a29eaa55cc235c8029807b2520976a937c30e9ee603f3bb566/ipython-5.7.0.tar.gz#sha256=8db43a7fb7619037c98626613ff08d03dda9d5d12c84814a4504c78c0da8323c">ipython-5.7.0.tar.gz</a><br/>
<a href="https://files.pythonhosted.org/packages/a9/2e/41dce4ed129057e05a555a7f9629aa2d5f81fdcd4d16568bc24b75a1d2c9/ipython-7.5.0-py3-none-any.whl#md5=dbdc53e3918f28fa335a173432402a00" data-requires-python="&gt;=3.5">ipython-7.5.0-py3-none-any.whl</a><br/>
<a href="https://files.pythonhosted.org/packages/75/74/9b0ef91c8e356c907bb12297000951acb804583b54eeaddc342c5bad4d96/ipython-7.5.0.tar.gz#sha256=e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26" data-requires-python="&gt;=3.5">ipython-7.5.0.tar.gz</a><br/>
</body> </body>
</html> </html>
<!--SERIAL 4837213--> <!--SERIAL 4837213-->
...@@ -205,3 +205,43 @@ def test_get_package_from_both_py2_and_py3_specific_wheels(): ...@@ -205,3 +205,43 @@ def test_get_package_from_both_py2_and_py3_specific_wheels():
package.requires[4].marker package.requires[4].marker
) )
assert 'sys_platform != "win32"' == str(package.requires[5].marker) assert 'sys_platform != "win32"' == str(package.requires[5].marker)
def test_get_package_with_dist_and_universal_py3_wheel():
repo = MockRepository()
package = repo.package("ipython", "7.5.0")
assert "ipython" == package.name
assert "7.5.0" == package.version.text
assert ">=3.5" == package.python_versions
expected = [
Dependency("appnope", "*"),
Dependency("backcall", "*"),
Dependency("colorama", "*"),
Dependency("decorator", "*"),
Dependency("jedi", ">=0.10"),
Dependency("pexpect", "*"),
Dependency("pickleshare", "*"),
Dependency("prompt-toolkit", ">=2.0.0,<2.1.0"),
Dependency("pygments", "*"),
Dependency("setuptools", ">=18.5"),
Dependency("traitlets", ">=4.2"),
Dependency("typing", "*"),
Dependency("win-unicode-console", ">=0.5"),
]
assert expected == sorted(package.requires, key=lambda dep: dep.name)
def test_get_package_retrieves_non_sha256_hashes():
repo = MockRepository()
package = repo.package("ipython", "7.5.0")
expected = [
"md5:dbdc53e3918f28fa335a173432402a00",
"e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26",
]
assert expected == package.hashes
...@@ -450,3 +450,22 @@ def test_remove_also_deactivates(tmp_dir, config, mocker): ...@@ -450,3 +450,22 @@ def test_remove_also_deactivates(tmp_dir, config, mocker):
envs = envs_file.read() envs = envs_file.read()
assert venv_name not in envs assert venv_name not in envs
def test_env_has_symlinks_on_nix(tmp_dir, config):
venv_path = Path(tmp_dir)
EnvManager(config).build_venv(str(venv_path))
venv = VirtualEnv(venv_path)
venv_available = False
try:
from venv import EnvBuilder
venv_available = True
except ImportError:
pass
if os.name != "nt" and venv_available:
assert os.path.islink(venv.python)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment