Skip to content
Open
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
13 changes: 10 additions & 3 deletions Doc/library/venv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ mechanisms for third-party virtual environment creators to customize environment
creation according to their needs, the :class:`EnvBuilder` class.

.. class:: EnvBuilder(system_site_packages=False, clear=False, \
symlinks=False, upgrade=False, with_pip=False, \
symlinks=None, upgrade=False, with_pip=False, \
prompt=None, upgrade_deps=False, \
*, scm_ignore_files=frozenset())

Expand All @@ -316,7 +316,8 @@ creation according to their needs, the :class:`EnvBuilder` class.
any existing target directory, before creating the environment.

* *symlinks* -- a boolean value indicating whether to attempt to symlink the
Python binary rather than copying.
Python binary rather than copying. If ``None``, the default is ``False`` on
Windows and ``True`` on other platforms, matching the :ref:`CLI <venv-cli>`.

* *upgrade* -- a boolean value which, if true, will upgrade an existing
environment with the running Python - for use when that Python has been
Expand Down Expand Up @@ -351,6 +352,9 @@ creation according to their needs, the :class:`EnvBuilder` class.
.. versionchanged:: 3.13
Added the ``scm_ignore_files`` parameter

.. versionchanged:: 3.16
The default value of *symlinks* is now platform-dependent.

:class:`EnvBuilder` may be used as a base class.

.. method:: create(env_dir)
Expand Down Expand Up @@ -521,7 +525,7 @@ creation according to their needs, the :class:`EnvBuilder` class.
There is also a module-level convenience function:

.. function:: create(env_dir, system_site_packages=False, clear=False, \
symlinks=False, with_pip=False, prompt=None, \
symlinks=None, with_pip=False, prompt=None, \
upgrade_deps=False, *, scm_ignore_files=frozenset())

Create an :class:`EnvBuilder` with the given keyword arguments, and call its
Expand All @@ -541,6 +545,9 @@ There is also a module-level convenience function:
.. versionchanged:: 3.13
Added the *scm_ignore_files* parameter

.. versionchanged:: 3.16
The default value of *symlinks* is now platform-dependent.

An example of extending ``EnvBuilder``
--------------------------------------

Expand Down
29 changes: 27 additions & 2 deletions Lib/test/test_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,31 @@ def test_defaults_with_pathlike(self):
self.run_with_capture(venv.create, FakePath(self.env_dir))
self._check_output_of_default_create()

def test_envbuilder_symlinks(self):
for kwargs, expected in (
({}, os.name != 'nt'),
({'symlinks': None}, os.name != 'nt'),
({'symlinks': True}, True),
({'symlinks': False}, False),
):
with self.subTest(kwargs=kwargs):
builder = venv.EnvBuilder(**kwargs)
self.assertIs(builder.symlinks, expected)

def test_create_symlinks(self):
for kwargs, expected in (
({}, os.name != 'nt'),
({'symlinks': None}, os.name != 'nt'),
({'symlinks': True}, True),
({'symlinks': False}, False),
):
with self.subTest(kwargs=kwargs):
with patch.object(venv.EnvBuilder, 'create', autospec=True) as create:
venv.create(self.env_dir, **kwargs)
builder, env_dir = create.call_args.args
self.assertIs(builder.symlinks, expected)
self.assertEqual(env_dir, self.env_dir)

def _check_output_of_default_create(self):
self.isdir(self.bindir)
self.isdir(self.include)
Expand All @@ -146,8 +171,7 @@ def _check_output_of_default_create(self):
self.assertIn('home = %s' % path, data)
self.assertIn('executable = %s' %
os.path.realpath(sys.executable), data)
copies = '' if os.name=='nt' else ' --copies'
cmd = (f'command = {sys.executable} -m venv{copies} --without-pip '
cmd = (f'command = {sys.executable} -m venv --without-pip '
f'--without-scm-ignore-files {self.env_dir}')
self.assertIn(cmd, data)
fn = self.get_env_file(self.bindir, self.exe)
Expand All @@ -156,6 +180,7 @@ def _check_output_of_default_create(self):
print('Contents of %r:' % bd)
print(' %r' % os.listdir(bd))
self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
self.assertEqual(os.path.islink(fn), os.name != 'nt' and can_symlink())

def test_config_file_command_key(self):
options = [
Expand Down
13 changes: 6 additions & 7 deletions Lib/venv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@ class EnvBuilder:
By default, the builder makes the system (global) site-packages dir
*un*available to the created environment.

If invoked using the Python -m option, the default is to use copying
on Windows platforms but symlinks elsewhere. If instantiated some
other way, the default is to *not* use symlinks.
By default, the builder uses copying on Windows platforms but symlinks
elsewhere, matching the behaviour when invoked using the Python -m option.

:param system_site_packages: If True, the system (global) site-packages
dir is available to created environments.
:param clear: If True, delete the contents of the environment directory if
it already exists, before environment creation.
:param symlinks: If True, attempt to symlink rather than copy files into
virtual environment.
virtual environment. If None, use the platform default.
:param upgrade: If True, upgrade an existing virtual environment.
:param with_pip: If True, ensure pip is installed in the virtual
environment
Expand All @@ -47,11 +46,11 @@ class EnvBuilder:
"""

def __init__(self, system_site_packages=False, clear=False,
symlinks=False, upgrade=False, with_pip=False, prompt=None,
symlinks=None, upgrade=False, with_pip=False, prompt=None,
upgrade_deps=False, *, scm_ignore_files=frozenset()):
self.system_site_packages = system_site_packages
self.clear = clear
self.symlinks = symlinks
self.symlinks = os.name != 'nt' if symlinks is None else symlinks
self.upgrade = upgrade
self.with_pip = with_pip
self.orig_prompt = prompt
Expand Down Expand Up @@ -607,7 +606,7 @@ def upgrade_dependencies(self, context):


def create(env_dir, system_site_packages=False, clear=False,
symlinks=False, with_pip=False, prompt=None, upgrade_deps=False,
symlinks=None, with_pip=False, prompt=None, upgrade_deps=False,
*, scm_ignore_files=frozenset()):
"""Create a virtual environment in a directory."""
builder = EnvBuilder(system_site_packages=system_site_packages,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The :class:`venv.EnvBuilder` and :func:`venv.create` APIs now use the
platform-dependent default for symlinks from :mod:`venv`'s command-line
interface.
Loading