Repository navigation
venv.EnvBuilder and venv.create should have the same symlinks defaults as python -m venv #129382
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.
Activity
zanieb commentedon Jan 29, 2025
cc @FFY00
FFY00 commentedon Jan 29, 2025
The CLI is meant to "just work" for users, while the programmatic API is more intentful, so I can see how it could make sense to have different defaults, but that said, I believe the state of support for symlinks has improved greatly since the PEP was written, so IMO it would make sense to change the default to always enable symlinks.
@vsajip @zooba, do you have any options/concerns?
vsajip commentedon Jan 30, 2025
Well, if we change the API defaults, isn't there some potential for breaking existing code? And this improved support for symlinks (presumably you mean on Windows) - as far as I'm aware, on Windows 10 you still need Administrator privileges to create symlinks. Is that not the case?
FFY00 commentedon Jan 30, 2025
We can make the default trying to create symlinks, and falling back to copy if it fails, but I'd like @zooba to confirm that the UX experience there wouldn't be negative.
zanieb commentedon Jan 30, 2025
I think there's a possibility for breakage, but using copied interpreters on Unix systems seems to be more problematic than symlinks so I would be surprised.
Why not just focus on having the same defaults as
python -m venvhere? We can consider separately if Windows should try symlinks and copy fallback. I'm imagining changing fromsymlinks=Falsetosymlinks=Nonein the constructor then, ifNone, inferring a default based on the operating system — not changingsymlinks=True.zooba commentedon Jan 30, 2025
Having it match the default of the platform would be fine, but I don't think we should have them enabled by default on Windows at this stage. They're not tested well enough and too likely to break for how widely used venv is (even if the programmatic interface isn't as widely used).
The proposal is to match the command line, and that's as far as we should go. Though I'll also note that changing a programmatic interface is more of a breaking change than the command line (as Vinay said), and that should be taken into account as well.
pythonGH-129382: change venv's API symlinks default to match the CLI
pythonGH-129382: change venv's API symlinks default to match the CLI
FFY00 commentedon Jan 31, 2025
Yeah, that sounds good. I opened GH-129493.
Since defaulting to copying the executable on Windows is not problematic, and we haven't gotten any request for the "symlink with fallback" option, I think we can leave it as-is.
Merge branch 'main' into pythongh-129382
zanieb commentedon Aug 13, 2026
This is still a surprising pain point for users, e.g., astral-sh/uv#21077
notatallshaw commentedon Aug 13, 2026
Also came up with pip's new venv isolation mode for building packages.
zanieb commentedon Aug 13, 2026
I'll try to pick up that stale pull request, I think we have consensus here but it's not moving forward.
gh-129382: Match venv API symlink defaults to the CLI (#155759)