瀏覽代碼

Refs #29973 -- Extracted helper functions from makemessages.

rsiemens 6 年之前
父節點
當前提交
bc9f0b3203
共有 3 個文件被更改,包括 57 次插入27 次删除
  1. 3 26
      django/core/management/commands/makemessages.py
  2. 30 0
      django/core/management/utils.py
  3. 24 1
      tests/user_commands/tests.py

+ 3 - 26
django/core/management/commands/makemessages.py

@@ -1,4 +1,3 @@
-import fnmatch
 import glob
 import os
 import re
@@ -12,7 +11,7 @@ from django.core.exceptions import ImproperlyConfigured
 from django.core.files.temp import NamedTemporaryFile
 from django.core.management.base import BaseCommand, CommandError
 from django.core.management.utils import (
-    find_command, handle_extensions, popen_wrapper,
+    find_command, handle_extensions, is_ignored_path, popen_wrapper,
 )
 from django.utils.encoding import DEFAULT_LOCALE_ENCODING
 from django.utils.functional import cached_property
@@ -454,35 +453,13 @@ class Command(BaseCommand):
         Get all files in the given root. Also check that there is a matching
         locale dir for each file.
         """
-        def is_ignored(path, ignore_patterns):
-            """
-            Check if the given path should be ignored or not.
-            """
-            filename = os.path.basename(path)
-
-            def ignore(pattern):
-                return fnmatch.fnmatchcase(filename, pattern) or fnmatch.fnmatchcase(path, pattern)
-
-            return any(ignore(pattern) for pattern in ignore_patterns)
-
-        ignore_patterns = [os.path.normcase(p) for p in self.ignore_patterns]
-        dir_suffixes = {'%s*' % path_sep for path_sep in {'/', os.sep}}
-        norm_patterns = []
-        for p in ignore_patterns:
-            for dir_suffix in dir_suffixes:
-                if p.endswith(dir_suffix):
-                    norm_patterns.append(p[:-len(dir_suffix)])
-                    break
-            else:
-                norm_patterns.append(p)
-
         all_files = []
         ignored_roots = []
         if self.settings_available:
             ignored_roots = [os.path.normpath(p) for p in (settings.MEDIA_ROOT, settings.STATIC_ROOT) if p]
         for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=self.symlinks):
             for dirname in dirnames[:]:
-                if (is_ignored(os.path.normpath(os.path.join(dirpath, dirname)), norm_patterns) or
+                if (is_ignored_path(os.path.normpath(os.path.join(dirpath, dirname)), self.ignore_patterns) or
                         os.path.join(os.path.abspath(dirpath), dirname) in ignored_roots):
                     dirnames.remove(dirname)
                     if self.verbosity > 1:
@@ -493,7 +470,7 @@ class Command(BaseCommand):
             for filename in filenames:
                 file_path = os.path.normpath(os.path.join(dirpath, filename))
                 file_ext = os.path.splitext(filename)[1]
-                if file_ext not in self.extensions or is_ignored(file_path, self.ignore_patterns):
+                if file_ext not in self.extensions or is_ignored_path(file_path, self.ignore_patterns):
                     if self.verbosity > 1:
                         self.stdout.write('ignoring file %s in %s\n' % (filename, dirpath))
                 else:

+ 30 - 0
django/core/management/utils.py

@@ -1,4 +1,6 @@
+import fnmatch
 import os
+from pathlib import Path
 from subprocess import PIPE, Popen
 
 from django.apps import apps as installed_apps
@@ -122,3 +124,31 @@ def get_command_line_option(argv, option):
         return None
     else:
         return options.value
+
+
+def normalize_path_patterns(patterns):
+    """Normalize an iterable of glob style patterns based on OS."""
+    patterns = [os.path.normcase(p) for p in patterns]
+    dir_suffixes = {'%s*' % path_sep for path_sep in {'/', os.sep}}
+    norm_patterns = []
+    for pattern in patterns:
+        for dir_suffix in dir_suffixes:
+            if pattern.endswith(dir_suffix):
+                norm_patterns.append(pattern[:-len(dir_suffix)])
+                break
+        else:
+            norm_patterns.append(pattern)
+    return norm_patterns
+
+
+def is_ignored_path(path, ignore_patterns):
+    """
+    Check if the given path should be ignored or not based on matching
+    one of the glob style `ignore_patterns`.
+    """
+    path = Path(path)
+
+    def ignore(pattern):
+        return fnmatch.fnmatchcase(path.name, pattern) or fnmatch.fnmatchcase(str(path), pattern)
+
+    return any(ignore(pattern) for pattern in normalize_path_patterns(ignore_patterns))

+ 24 - 1
tests/user_commands/tests.py

@@ -8,7 +8,8 @@ from django.apps import apps
 from django.core import management
 from django.core.management import BaseCommand, CommandError, find_commands
 from django.core.management.utils import (
-    find_command, get_random_secret_key, popen_wrapper,
+    find_command, get_random_secret_key, is_ignored_path,
+    normalize_path_patterns, popen_wrapper,
 )
 from django.db import connection
 from django.test import SimpleTestCase, override_settings
@@ -268,3 +269,25 @@ class UtilsTests(SimpleTestCase):
         self.assertEqual(len(key), 50)
         for char in key:
             self.assertIn(char, 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)')
+
+    def test_is_ignored_path_true(self):
+        patterns = (
+            ['foo/bar/baz'],
+            ['baz'],
+            ['foo/bar/baz'],
+            ['*/baz'],
+            ['*'],
+            ['b?z'],
+            ['[abc]az'],
+            ['*/ba[!z]/baz'],
+        )
+        for ignore_patterns in patterns:
+            with self.subTest(ignore_patterns=ignore_patterns):
+                self.assertIs(is_ignored_path('foo/bar/baz', ignore_patterns=ignore_patterns), True)
+
+    def test_is_ignored_path_false(self):
+        self.assertIs(is_ignored_path('foo/bar/baz', ignore_patterns=['foo/bar/bat', 'bar', 'flub/blub']), False)
+
+    def test_normalize_path_patterns_truncates_wildcard_base(self):
+        expected = [os.path.normcase(p) for p in ['foo/bar', 'bar/*/']]
+        self.assertEqual(normalize_path_patterns(['foo/bar/*', 'bar/*/']), expected)