Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ which will give you something similar to this:
cache-dir = "/path/to/cache/directory"
virtualenvs.create = true
virtualenvs.in-project = false
virtualenvs.system-packages = false
virtualenvs.path = "{cache-dir}/virtualenvs" # /path/to/cache/directory/virtualenvs
```

Expand Down Expand Up @@ -112,6 +113,12 @@ Defaults to `true`.
Create the virtualenv inside the project's root directory.
Defaults to `false`.

### `virtualenvs.system-packages`: boolean

Give the virtual environment access to the system site-packages directory.
Applies on virtualenv creation.
Defaults to `false`.

### `virtualenvs.path`: string

Directory where virtual environments will be created.
Expand Down
13 changes: 11 additions & 2 deletions poetry/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Config(object):
"virtualenvs": {
"create": True,
"in-project": False,
"system-packages": False,
"path": os.path.join("{cache-dir}", "virtualenvs"),
},
}
Expand Down Expand Up @@ -130,14 +131,22 @@ def process(self, value): # type: (Any) -> Any
return re.sub(r"{(.+?)}", lambda m: self.get(m.group(1)), value)

def _get_validator(self, name): # type: (str) -> Callable
if name in {"virtualenvs.create", "virtualenvs.in-project"}:
if name in {
"virtualenvs.create",
"virtualenvs.in-project",
"virtualenvs.system-packages",
}:
return boolean_validator

if name == "virtualenvs.path":
return str

def _get_normalizer(self, name): # type: (str) -> Callable
if name in {"virtualenvs.create", "virtualenvs.in-project"}:
if name in {
"virtualenvs.create",
"virtualenvs.in-project",
"virtualenvs.system-packages",
}:
return boolean_normalizer

if name == "virtualenvs.path":
Expand Down
5 changes: 5 additions & 0 deletions poetry/console/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ def unique_config_values(self):
),
"virtualenvs.create": (boolean_validator, boolean_normalizer, True),
"virtualenvs.in-project": (boolean_validator, boolean_normalizer, False),
"virtualenvs.system-packages": (
boolean_validator,
boolean_normalizer,
False,
),
"virtualenvs.path": (
str,
lambda val: str(Path(val)),
Expand Down
23 changes: 18 additions & 5 deletions poetry/utils/env.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import base64
import functools
import hashlib
import json
import os
Expand Down Expand Up @@ -483,6 +484,10 @@ def create_venv(

create_venv = self._poetry.config.get("virtualenvs.create")
root_venv = self._poetry.config.get("virtualenvs.in-project")
system_packages = self._poetry.config.get("virtualenvs.system-packages")

if system_packages and io.is_debug():
io.write_line("<debug>Including system site-packages</>")

venv_path = self._poetry.config.get("virtualenvs.path")
if root_venv:
Expand Down Expand Up @@ -607,7 +612,9 @@ def create_venv(
"Creating virtualenv <c1>{}</> in {}".format(name, str(venv_path))
)

self.build_venv(str(venv), executable=executable)
self.build_venv(
str(venv), executable=executable, system_packages=system_packages
)
else:
if force:
if not env.is_sane():
Expand All @@ -620,7 +627,9 @@ def create_venv(
"Recreating virtualenv <c1>{}</> in {}".format(name, str(venv))
)
self.remove_venv(str(venv))
self.build_venv(str(venv), executable=executable)
self.build_venv(
str(venv), executable=executable, system_packages=system_packages
)
elif io.is_very_verbose():
io.write_line("Virtualenv <c1>{}</> already exists.".format(name))

Expand All @@ -643,7 +652,7 @@ def create_venv(
return VirtualEnv(venv)

@classmethod
def build_venv(cls, path, executable=None):
def build_venv(cls, path, executable=None, system_packages=False):
if executable is not None:
# Create virtualenv by using an external executable
try:
Expand All @@ -667,13 +676,17 @@ def build_venv(cls, path, executable=None):
else:
use_symlinks = True

builder = EnvBuilder(with_pip=True, symlinks=use_symlinks)
builder = EnvBuilder(
system_site_packages=system_packages,
with_pip=True,
symlinks=use_symlinks,
)
build = builder.create
except ImportError:
# We fallback on virtualenv for Python 2.7
from virtualenv import create_environment

build = create_environment
build = functools.partial(create_environment, site_packages=system_packages)

build(path)

Expand Down
6 changes: 4 additions & 2 deletions tests/console/commands/env/test_use.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
CWD = Path(__file__).parent.parent / "fixtures" / "simple_project"


def build_venv(path, executable=None):
def build_venv(path, executable=None, system_packages=False):
os.mkdir(path)


Expand Down Expand Up @@ -61,7 +61,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(app, tmp_dir, m
)

m.assert_called_with(
os.path.join(tmp_dir, "{}-py3.7".format(venv_name)), executable="python3.7"
os.path.join(tmp_dir, "{}-py3.7".format(venv_name)),
executable="python3.7",
system_packages=False,
)

envs_file = TomlFile(Path(tmp_dir) / "envs.toml")
Expand Down
3 changes: 3 additions & 0 deletions tests/console/commands/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def test_list_displays_default_value_if_not_set(app, config):
virtualenvs.create = true
virtualenvs.in-project = false
virtualenvs.path = {path} # /foo{sep}virtualenvs
virtualenvs.system-packages = false
""".format(
path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep
)
Expand All @@ -35,6 +36,7 @@ def test_list_displays_set_get_setting(app, config):
virtualenvs.create = false
virtualenvs.in-project = false
virtualenvs.path = {path} # /foo{sep}virtualenvs
virtualenvs.system-packages = false
""".format(
path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep
)
Expand Down Expand Up @@ -82,6 +84,7 @@ def test_list_displays_set_get_local_setting(app, config):
virtualenvs.create = false
virtualenvs.in-project = false
virtualenvs.path = {path} # /foo{sep}virtualenvs
virtualenvs.system-packages = false
""".format(
path=json.dumps(os.path.join("{cache-dir}", "virtualenvs")), sep=os.path.sep
)
Expand Down
1 change: 1 addition & 0 deletions tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,4 @@ def test_create_poetry_with_local_config(fixture_dir):

assert not poetry.config.get("virtualenvs.in-project")
assert not poetry.config.get("virtualenvs.create")
assert not poetry.config.get("virtualenvs.system-packages")
40 changes: 33 additions & 7 deletions tests/utils/test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def test_env_get_in_project_venv(manager, poetry):
shutil.rmtree(str(venv.path))


def build_venv(path, executable=None):
def build_venv(path, executable=None, system_packages=False):
os.mkdir(path)


Expand Down Expand Up @@ -137,7 +137,9 @@ def test_activate_activates_non_existing_virtualenv_no_envs_file(
venv_name = EnvManager.generate_env_name("simple-project", str(poetry.file.parent))

m.assert_called_with(
os.path.join(tmp_dir, "{}-py3.7".format(venv_name)), executable="python3.7"
os.path.join(tmp_dir, "{}-py3.7".format(venv_name)),
executable="python3.7",
system_packages=False,
)

envs_file = TomlFile(Path(tmp_dir) / "envs.toml")
Expand Down Expand Up @@ -255,7 +257,9 @@ def test_activate_activates_different_virtualenv_with_envs_file(
env = manager.activate("python3.6", NullIO())

m.assert_called_with(
os.path.join(tmp_dir, "{}-py3.6".format(venv_name)), executable="python3.6"
os.path.join(tmp_dir, "{}-py3.6".format(venv_name)),
executable="python3.6",
system_packages=False,
)

assert envs_file.exists()
Expand Down Expand Up @@ -307,7 +311,9 @@ def test_activate_activates_recreates_for_different_patch(
env = manager.activate("python3.7", NullIO())

build_venv_m.assert_called_with(
os.path.join(tmp_dir, "{}-py3.7".format(venv_name)), executable="python3.7"
os.path.join(tmp_dir, "{}-py3.7".format(venv_name)),
executable="python3.7",
system_packages=False,
)
remove_venv_m.assert_called_with(
os.path.join(tmp_dir, "{}-py3.7".format(venv_name))
Expand Down Expand Up @@ -596,7 +602,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_generic_
manager.create_venv(NullIO())

m.assert_called_with(
str(Path("/foo/virtualenvs/{}-py3.7".format(venv_name))), executable="python3"
str(Path("/foo/virtualenvs/{}-py3.7".format(venv_name))),
executable="python3",
system_packages=False,
)


Expand All @@ -620,7 +628,9 @@ def test_create_venv_tries_to_find_a_compatible_python_executable_using_specific
manager.create_venv(NullIO())

m.assert_called_with(
str(Path("/foo/virtualenvs/{}-py3.8".format(venv_name))), executable="python3.8"
str(Path("/foo/virtualenvs/{}-py3.8".format(venv_name))),
executable="python3.8",
system_packages=False,
)


Expand Down Expand Up @@ -711,6 +721,7 @@ def test_create_venv_uses_patch_version_to_detect_compatibility(
)
),
executable=None,
system_packages=False,
)


Expand Down Expand Up @@ -750,6 +761,7 @@ def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable(
)
),
executable="python{}.{}".format(version.major, version.minor - 1),
system_packages=False,
)


Expand Down Expand Up @@ -781,7 +793,9 @@ def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir(
manager.activate("python3.7", NullIO())

m.assert_called_with(
os.path.join(str(poetry.file.parent), ".venv"), executable="python3.7"
os.path.join(str(poetry.file.parent), ".venv"),
executable="python3.7",
system_packages=False,
)

envs_file = TomlFile(Path(tmp_dir) / "virtualenvs" / "envs.toml")
Expand Down Expand Up @@ -843,3 +857,15 @@ def test_env_site_packages_should_raise_an_error_if_no_site_packages(tmp_dir):

with pytest.raises(RuntimeError):
env.site_packages


def test_env_system_packages(tmp_path, config):
venv_path = tmp_path / "venv"
EnvManager(config).build_venv(str(venv_path), system_packages=True)
pyvenv_cfg = venv_path / "pyvenv.cfg"
if sys.version_info >= (3, 3):
assert "include-system-site-packages = true" in pyvenv_cfg.read_text()
elif (2, 6) < sys.version_info < (3, 0):
assert not venv_path.joinpath(
"lib", "python2.7", "no-global-site-packages.txt"
).exists()