From 28869f6af835a39eae96453c0e5ab3dc01f7b00e Mon Sep 17 00:00:00 2001 From: Yoriyasu Yano <430092+yorinasub17@users.noreply.github.com> Date: Sat, 20 Apr 2019 14:08:59 -0700 Subject: [PATCH] When iterating over Zipfiles, always use the Unix file separator to fix a Windows issue (#638) Pex doesn't officially support windows, but there seems to be a regression in pex 1.6.0 with windows with respect to the way the vendoring works. Specifically, the regex for iterating the zip path in the vendoring uses the os separator, which has two problems on windows (where the separator is `\`): 1. `\` is an escape sequence, so unless the separator is added twice in the string, it uses it to escape the character immediately after the separator, causing the regex compilation to fail. 1. The path in the zip file uses unix separators (`/`), so using the os separator is actually wrong. This PR addresses this by updating the regex to replace windows separators (`\`) in the prefix var with the unix separator. --- pex/third_party/__init__.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pex/third_party/__init__.py b/pex/third_party/__init__.py index f25a700da..46837b06c 100644 --- a/pex/third_party/__init__.py +++ b/pex/third_party/__init__.py @@ -98,9 +98,13 @@ def containing(cls, root): prefix = '' path = root while path: + # We use '/' here because the zip file format spec specifies that paths must use + # forward slashes. See section 4.4.17 of + # https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT. if zipfile.is_zipfile(path): - return cls(zipfile_path=path, prefix=prefix + os.sep if prefix else '') - prefix = os.path.join(prefix, os.path.basename(path)) + return cls(zipfile_path=path, prefix='{}/'.format(prefix) if prefix else '') + path_base = os.path.basename(path) + prefix = '{}/{}'.format(path_base, prefix) if prefix else path_base path = os.path.dirname(path) raise ValueError('Could not find the zip file housing {}'.format(root)) @@ -114,10 +118,11 @@ def iter_root_packages(self, relpath): yield package def _filter_names(self, relpath, pattern, group): - pat = re.compile(r'^{prefix}{pattern}$' - .format(prefix=self.prefix + ((relpath + os.sep) if relpath else ''), - pattern=pattern)) - + # We use '/' here because the zip file format spec specifies that paths must use + # forward slashes. See section 4.4.17 of + # https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT. + relpath_pat = '' if not relpath else '{}/'.format(relpath.replace(os.sep, '/')) + pat = re.compile(r'^{}{}{}$'.format(self.prefix, relpath_pat, pattern)) with contextlib.closing(zipfile.ZipFile(self.zipfile_path)) as zf: for name in zf.namelist(): match = pat.match(name)