Description
When running python -m venv
to create a venv, the choice of whether to symlink or copy the Python executable is based on the current OS:
if os.name == 'nt':
use_symlinks = False
else:
use_symlinks = True
group = parser.add_mutually_exclusive_group()
group.add_argument('--symlinks', default=use_symlinks,
action='store_true', dest='symlinks',
help='Try to use symlinks rather than copies, '
'when symlinks are not the default for '
'the platform.')
group.add_argument('--copies', default=not use_symlinks,
action='store_false', dest='symlinks',
help='Try to use copies rather than symlinks, '
'even when symlinks are the default for '
'the platform.')
When using the programmatic API, however, both venv.EnvBuilder()
and venv.create()
default to symlinks=False
.
I don't understand the reason for this discrepancy. The PEP proposing venv
has a section addressing copies versus symlinks that makes a pretty good argument that symlinks should be preferred whenever possible, and #59486 (comment) makes the same argument, saying "Following discussions on python-dev, the default is always to symlink, except on Windows (no support for true symlinks on XP and older) and Mac OS X (problems with framework builds)." (I couldn't find those python-dev discussions.) Nothing in this argument seems like it should apply only to the CLI interface and not to the API.
I tracked down the commit that added the OS-specific behavior from @vsajip's dev repo, and it doesn't explain why this was only changed in the CLI. (Before that commit, both the CLI and the API defaulted to copies.) While the behavior discrepancy is documented, the documentation seems to be retroactively stating the behavior of the code (#60582, #76700) as opposed to documenting an actual intention.
Furthermore, as discussed in astral-sh/python-build-standalone#381, copy-based venvs don't work properly if your Python interpreter is built to link libpython.so
and is relocatable (can be installed at any arbitrary location on the filesystem). This leads to a confusing discrepancy in behavior between python -m venv
, which works fine with these types of interpreters, and the API. (As I note in that issue, Apple seems to have run into this with the Python distributions shipped via Xcode and has patched the venv
module to behave consistently between the CLI and API and also to throw an error in the copy case if they detect it's about to not work.)
I think it would be good to change the behavior of the CLI and the API to be consistent, i.e., refactor the if os.name == 'nt'
logic into venv.EnvBuilder.__init__()
and have python -m venv
not override it. I don't think there should be a serious backwards-compatibility risk in changing the default argument: in the cases where there's a noticeable difference, it's probably what users expect anyway, and if it does cause an issue in someone's code, it's easy (and clearer) to specifically call venv.create(symlinks=False)
.
I can send a PR with a NEWS entry if this change seems reasonable / nobody remembers why the discrepancy exists.