Skip to content
Merged
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
2 changes: 2 additions & 0 deletions docs/changelog/2543.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Prevent ``PermissionError`` when using venv creator on systems that deliver files without user write
permission - by :user:`kulikjak`.
4 changes: 4 additions & 0 deletions src/virtualenv/activation/via_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def _generate(self, replacements, templates, to_folder, creator):
for template in templates:
text = self.instantiate_template(replacements, template, creator)
dest = to_folder / self.as_name(template)
# remove the file if it already exists - this prevents permission
# errors when the dest is not writable
if dest.exists():
dest.unlink()
# use write_bytes to avoid platform specific line normalization (\n -> \r\n)
dest.write_bytes(text.encode("utf-8"))
generated.append(dest)
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/create/test_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,3 +611,25 @@ def _get_sys_path(flag=None):
assert non_python_path == [i for i in base if i not in extra_as_python_path]
else:
assert base == extra_all


# Make sure that the venv creator works on systems where vendor-delivered files
# (specifically venv scripts delivered with Python itself) are not writable.
#
# https://github.com/pypa/virtualenv/issues/2419
@pytest.mark.skipif("venv" not in CURRENT_CREATORS, reason="test needs venv creator")
def test_venv_creator_without_write_perms(tmp_path, mocker):
from virtualenv.run.session import Session

prev = Session._create

def func(self):
prev(self)
scripts_dir = self.creator.dest / "bin"
for script in scripts_dir.glob("*ctivate*"):
script.chmod(stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH)

mocker.patch("virtualenv.run.session.Session._create", side_effect=func, autospec=True)

cmd = [str(tmp_path), "--seeder", "app-data", "--without-pip", "--creator", "venv"]
cli_run(cmd)