Skip to content

venv.EnvBuilder and venv.create should have the same symlinks defaults as python -m venv #129382

Closed

Description

@geofft

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.

Linked PRs

Activity

  1. added
    topic-venvRelated to the venv module
    stdlibStandard Library Python modules in the Lib/ directory
    on Jan 28, 2025
  2. zanieb commented on Jan 29, 2025

    @zanieb
    Contributor
  3. FFY00 commented on Jan 29, 2025

    @FFY00
    Member

    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?

  4. vsajip commented on Jan 30, 2025

    @vsajip
    Member

    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?

  5. FFY00 commented on Jan 30, 2025

    @FFY00
    Member

    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.

  6. zanieb commented on Jan 30, 2025

    @zanieb
    Contributor

    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.

    We can make the default trying to create symlinks, and falling back to copy if it fails,

    Why not just focus on having the same defaults as python -m venv here? We can consider separately if Windows should try symlinks and copy fallback. I'm imagining changing from symlinks=False to symlinks=None in the constructor then, if None, inferring a default based on the operating system — not changing symlinks=True.

  7. zooba commented on Jan 30, 2025

    @zooba
    Member

    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.

  8. added 2 commits that reference this issue on Jan 31, 2025
  9. FFY00 commented on Jan 31, 2025

    @FFY00
    Member

    Why not just focus on having the same defaults as python -m venv here?

    Yeah, that sounds good. I opened GH-129493.

    We can consider separately if Windows should try symlinks and copy fallback. I'm imagining changing from symlinks=False to symlinks=None in the constructor then, if None, inferring a default based on the operating system — not changing symlinks=True.

    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.

  10. zanieb commented on Aug 13, 2026

    @zanieb
    Contributor

    This is still a surprising pain point for users, e.g., astral-sh/uv#21077

  11. notatallshaw commented on Aug 13, 2026

    @notatallshaw
    Contributor

    Also came up with pip's new venv isolation mode for building packages.

  12. zanieb commented on Aug 13, 2026

    @zanieb
    Contributor

    I'll try to pick up that stale pull request, I think we have consensus here but it's not moving forward.

  13. added a commit that references this issue on Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytopic-venvRelated to the venv module

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions