Skip to content

Commit

Permalink
Shorten path on image build if needed (Windows with no long path supp…
Browse files Browse the repository at this point in the history
…ort)

Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
  • Loading branch information
gaborbernat committed Dec 23, 2020
1 parent 88101a4 commit 42f1be6
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/changelog/2036.bugfix.rst
@@ -0,0 +1 @@
Bump embed pip to ``20.3.3``, setuptools to ``51.1.0`` and wheel to ``0.36.2`` - by :user:`gaborbernat`.
15 changes: 15 additions & 0 deletions src/virtualenv/seed/embed/via_app_data/pip_install/base.py
Expand Up @@ -51,13 +51,28 @@ def build_image(self):
# 1. first extract the wheel
logging.debug("build install image for %s to %s", self._wheel.name, self._image_dir)
with zipfile.ZipFile(str(self._wheel)) as zip_ref:
self._shorten_path_if_needed(zip_ref)
zip_ref.extractall(str(self._image_dir))
self._extracted = True
# 2. now add additional files not present in the distribution
new_files = self._generate_new_files()
# 3. finally fix the records file
self._fix_records(new_files)

def _shorten_path_if_needed(self, zip_ref):
if os.name == "nt":
to_folder = str(self._image_dir)
# https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
zip_max_len = max(len(i) for i in zip_ref.namelist())
path_len = zip_max_len + len(to_folder)
if path_len > 260:
self._image_dir.mkdir(exist_ok=True) # to get a short path must exist

from virtualenv.util.path import get_short_path_name

to_folder = get_short_path_name(to_folder)
self._image_dir = Path(to_folder)

def _records_text(self, files):
record_data = "\n".join(
"{},,".format(os.path.relpath(ensure_text(str(rec)), ensure_text(str(self._image_dir)))) for rec in files
Expand Down
2 changes: 2 additions & 0 deletions src/virtualenv/util/path/__init__.py
Expand Up @@ -3,6 +3,7 @@
from ._pathlib import Path
from ._permission import make_exe, set_tree
from ._sync import copy, copytree, ensure_dir, safe_delete, symlink
from ._win import get_short_path_name

__all__ = (
"ensure_dir",
Expand All @@ -13,4 +14,5 @@
"make_exe",
"set_tree",
"safe_delete",
"get_short_path_name",
)
19 changes: 19 additions & 0 deletions src/virtualenv/util/path/_win.py
@@ -0,0 +1,19 @@
def get_short_path_name(long_name):
"""
Gets the short path name of a given long path.
http://stackoverflow.com/a/23598461/200291
"""
import ctypes
from ctypes import wintypes

_GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW
_GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD]
_GetShortPathNameW.restype = wintypes.DWORD
output_buf_size = 0
while True:
output_buf = ctypes.create_unicode_buffer(output_buf_size)
needed = _GetShortPathNameW(long_name, output_buf, output_buf_size)
if output_buf_size >= needed:
return output_buf.value
else:
output_buf_size = needed
3 changes: 0 additions & 3 deletions tests/conftest.py
Expand Up @@ -335,9 +335,6 @@ def current_fastest(current_creators):
@pytest.fixture(scope="session")
def session_app_data(tmp_path_factory):
temp_folder = tmp_path_factory.mktemp("session-app-data")
# check long path support
test_long_path_support = temp_folder / ("a" * (270 - len(str(temp_folder))))
test_long_path_support.write_text("a")
app_data = AppDataDiskFolder(folder=str(temp_folder))
with change_env_var(str("VIRTUALENV_OVERRIDE_APP_DATA"), str(app_data.lock.path)):
yield app_data
Expand Down

0 comments on commit 42f1be6

Please sign in to comment.