ソースを参照

Fix excluding directories extracted from wheels

Closes gh-168
Thomas Kluyver 6 年 前
コミット
b7e3f5330c
2 ファイル変更38 行追加8 行削除
  1. 16 2
      nsist/tests/test_local_wheels.py
  2. 22 6
      nsist/wheels.py

+ 16 - 2
nsist/tests/test_local_wheels.py

@@ -3,11 +3,12 @@ import os
 from pathlib import Path
 import platform
 import subprocess
+from zipfile import ZipFile
 
 import pytest
-from testpath import assert_isfile, assert_isdir
+from testpath import assert_isfile, assert_isdir, assert_not_path_exists
 
-from nsist.wheels import WheelGetter
+from nsist.wheels import WheelGetter, extract_wheel
 
 # To exclude tests requiring network on an unplugged machine, use: pytest -m "not network"
 
@@ -85,3 +86,16 @@ def test_useless_wheel_glob_path_raise(tmpdir):
 
     with pytest.raises(ValueError, match='does not match any files'):
         wg.get_globs()
+
+def test_extract_exclude_folder(tmpdir):
+    whl_file = str(tmpdir / 'foo.whl')
+    pkgs = tmpdir.mkdir('pkgs')
+
+    with ZipFile(whl_file, 'w') as zf:
+        zf.writestr('foo/bar.txt', b'blah')
+        zf.writestr('foo/bar/abc.txt', b'blah')
+
+    extract_wheel(whl_file, str(pkgs), exclude=['pkgs/foo/bar'])
+
+    assert_isfile(str(pkgs / 'foo' / 'bar.txt'))
+    assert_not_path_exists(str(pkgs / 'foo' / 'bar'))

+ 22 - 6
nsist/wheels.py

@@ -222,10 +222,9 @@ def extract_wheel(whl_file, target_dir, exclude=None):
     td = Path(mkdtemp())
     with zipfile.ZipFile(str(whl_file), mode='r') as zf:
         if exclude:
-            basename = Path(Path(target_dir).name)
+            exclude_regexen = make_exclude_regexen(exclude)
             for zpath in zf.namelist():
-                path = basename / zpath
-                if is_excluded(path, exclude):
+                if is_excluded('pkgs/' + zpath, exclude_regexen):
                     continue  # Skip excluded paths
                 zf.extract(zpath, path=str(td))
         else:
@@ -324,10 +323,27 @@ class WheelGetter:
         self.got_distributions[distribution] = whl_path
 
 
-def is_excluded(path, exclude):
+def make_exclude_regexen(exclude_patterns):
+    """Translate exclude glob patterns to regex pattern objects.
+
+    Handles matching files under a named directory.
+    """
+    re_pats = set()
+    for pattern in exclude_patterns:
+        re_pats.add(fnmatch.translate(pattern))
+        if not pattern.endswith('*'):
+            # Also use the pattern as a directory name and match anything
+            # under that directory.
+            suffix = '*' if pattern.endswith('/') else '/*'
+            re_pats.add(fnmatch.translate(pattern + suffix))
+
+    return [re.compile(p) for p in sorted(re_pats)]
+
+
+def is_excluded(path, exclude_regexen):
     """Return True if path matches an exclude pattern"""
     path = normalize_path(path)
-    for pattern in (exclude or ()):
-        if fnmatch.fnmatch(path, pattern):
+    for re_pattern in exclude_regexen:
+        if re_pattern.match(path):
             return True
     return False