Fixed #17628 -- Extended makemessages command to also ignore directories when walking, not only when looking for files. Thanks, Claude Paroz.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17441 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2012-02-04 18:27:24 +00:00
parent 62dc16dcd8
commit c6065545ef
2 changed files with 48 additions and 32 deletions

View File

@ -7,6 +7,7 @@ from itertools import dropwhile
from optparse import make_option from optparse import make_option
from subprocess import PIPE, Popen from subprocess import PIPE, Popen
import django
from django.core.management.base import CommandError, NoArgsCommand from django.core.management.base import CommandError, NoArgsCommand
from django.utils.text import get_text_list from django.utils.text import get_text_list
from django.utils.jslex import prepare_js_for_gettext from django.utils.jslex import prepare_js_for_gettext
@ -44,11 +45,23 @@ def _popen(cmd):
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt', universal_newlines=True) p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt', universal_newlines=True)
return p.communicate() return p.communicate()
def walk(root, topdown=True, onerror=None, followlinks=False): def walk(root, topdown=True, onerror=None, followlinks=False,
ignore_patterns=[], verbosity=0, stdout=sys.stdout):
""" """
A version of os.walk that can follow symlinks for Python < 2.6 A version of os.walk that can follow symlinks for Python < 2.6
""" """
dir_suffix = '%s*' % os.sep
norm_patterns = map(lambda p: p.endswith(dir_suffix)
and p[:-len(dir_suffix)] or p, ignore_patterns)
for dirpath, dirnames, filenames in os.walk(root, topdown, onerror): for dirpath, dirnames, filenames in os.walk(root, topdown, onerror):
remove_dirs = []
for dirname in dirnames:
if is_ignored(os.path.normpath(os.path.join(dirpath, dirname)), norm_patterns):
remove_dirs.append(dirname)
for dirname in remove_dirs:
dirnames.remove(dirname)
if verbosity > 1:
stdout.write('ignoring directory %s\n' % dirname)
yield (dirpath, dirnames, filenames) yield (dirpath, dirnames, filenames)
if followlinks: if followlinks:
for d in dirnames: for d in dirnames:
@ -66,29 +79,29 @@ def is_ignored(path, ignore_patterns):
return True return True
return False return False
def find_files(root, ignore_patterns, verbosity, symlinks=False): def find_files(root, ignore_patterns, verbosity, stdout=sys.stdout, symlinks=False):
""" """
Helper function to get all files in the given root. Helper function to get all files in the given root.
""" """
all_files = [] all_files = []
for (dirpath, dirnames, filenames) in walk(".", followlinks=symlinks): for (dirpath, dirnames, filenames) in walk(".", followlinks=symlinks,
for f in filenames: ignore_patterns=ignore_patterns, verbosity=verbosity, stdout=stdout):
norm_filepath = os.path.normpath(os.path.join(dirpath, f)) for filename in filenames:
norm_filepath = os.path.normpath(os.path.join(dirpath, filename))
if is_ignored(norm_filepath, ignore_patterns): if is_ignored(norm_filepath, ignore_patterns):
if verbosity > 1: if verbosity > 1:
sys.stdout.write('ignoring file %s in %s\n' % (f, dirpath)) stdout.write('ignoring file %s in %s\n' % (f, dirpath))
else: else:
all_files.extend([(dirpath, f)]) all_files.extend([(dirpath, filename)])
all_files.sort() all_files.sort()
return all_files return all_files
def copy_plural_forms(msgs, locale, domain, verbosity): def copy_plural_forms(msgs, locale, domain, verbosity, stdout=sys.stdout):
""" """
Copies plural forms header contents from a Django catalog of locale to Copies plural forms header contents from a Django catalog of locale to
the msgs string, inserting it at the right place. msgs should be the the msgs string, inserting it at the right place. msgs should be the
contents of a newly created .po file. contents of a newly created .po file.
""" """
import django
django_dir = os.path.normpath(os.path.join(os.path.dirname(django.__file__))) django_dir = os.path.normpath(os.path.join(os.path.dirname(django.__file__)))
if domain == 'djangojs': if domain == 'djangojs':
domains = ('djangojs', 'django') domains = ('djangojs', 'django')
@ -100,7 +113,7 @@ def copy_plural_forms(msgs, locale, domain, verbosity):
m = plural_forms_re.search(open(django_po, 'rU').read()) m = plural_forms_re.search(open(django_po, 'rU').read())
if m: if m:
if verbosity > 1: if verbosity > 1:
sys.stderr.write("copying plural forms: %s\n" % m.group('value')) stdout.write("copying plural forms: %s\n" % m.group('value'))
lines = [] lines = []
seen = False seen = False
for line in msgs.split('\n'): for line in msgs.split('\n'):
@ -132,8 +145,8 @@ def write_pot_file(potfile, msgs, file, work_file, is_templatized):
finally: finally:
f.close() f.close()
def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap, def process_file(file, dirpath, potfile, domain, verbosity,
location): extensions, wrap, location, stdout=sys.stdout):
""" """
Extract translatable literals from :param file: for :param domain: Extract translatable literals from :param file: for :param domain:
creating or updating the :param potfile: POT file. creating or updating the :param potfile: POT file.
@ -144,7 +157,7 @@ def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
from django.utils.translation import templatize from django.utils.translation import templatize
if verbosity > 1: if verbosity > 1:
sys.stdout.write('processing file %s in %s\n' % (file, dirpath)) stdout.write('processing file %s in %s\n' % (file, dirpath))
_, file_ext = os.path.splitext(file) _, file_ext = os.path.splitext(file)
if domain == 'djangojs' and file_ext in extensions: if domain == 'djangojs' and file_ext in extensions:
is_templatized = True is_templatized = True
@ -162,10 +175,8 @@ def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
'xgettext -d %s -L C %s %s --keyword=gettext_noop ' 'xgettext -d %s -L C %s %s --keyword=gettext_noop '
'--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 ' '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
'--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 ' '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 '
'--from-code UTF-8 --add-comments=Translators -o - "%s"' % ( '--from-code UTF-8 --add-comments=Translators -o - "%s"' %
domain, wrap, location, work_file (domain, wrap, location, work_file))
)
)
elif domain == 'django' and (file_ext == '.py' or file_ext in extensions): elif domain == 'django' and (file_ext == '.py' or file_ext in extensions):
thefile = file thefile = file
orig_file = os.path.join(dirpath, file) orig_file = os.path.join(dirpath, file)
@ -187,9 +198,8 @@ def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
'--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 ' '--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 '
'--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 ' '--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 '
'--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 ' '--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 '
'--add-comments=Translators -o - "%s"' % ( '--add-comments=Translators -o - "%s"' %
domain, wrap, location, work_file) (domain, wrap, location, work_file))
)
else: else:
return return
msgs, errors = _popen(cmd) msgs, errors = _popen(cmd)
@ -206,8 +216,8 @@ def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
if is_templatized: if is_templatized:
os.unlink(work_file) os.unlink(work_file)
def write_po_file(pofile, potfile, domain, locale, verbosity, def write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
copy_pforms, wrap, location, no_obsolete): copy_pforms, wrap, location, no_obsolete):
""" """
Creates of updates the :param pofile: PO file for :param domain: and :param Creates of updates the :param pofile: PO file for :param domain: and :param
locale:. Uses contents of the existing :param potfile:. locale:. Uses contents of the existing :param potfile:.
@ -232,7 +242,7 @@ def write_po_file(pofile, potfile, domain, locale, verbosity,
raise CommandError( raise CommandError(
"errors happened while running msgmerge\n%s" % errors) "errors happened while running msgmerge\n%s" % errors)
elif copy_pforms: elif copy_pforms:
msgs = copy_plural_forms(msgs, locale, domain, verbosity) msgs = copy_plural_forms(msgs, locale, domain, verbosity, stdout)
msgs = msgs.replace( msgs = msgs.replace(
"#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % domain, "") "#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % domain, "")
f = open(pofile, 'wb') f = open(pofile, 'wb')
@ -250,7 +260,7 @@ def write_po_file(pofile, potfile, domain, locale, verbosity,
def make_messages(locale=None, domain='django', verbosity=1, all=False, def make_messages(locale=None, domain='django', verbosity=1, all=False,
extensions=None, symlinks=False, ignore_patterns=None, no_wrap=False, extensions=None, symlinks=False, ignore_patterns=None, no_wrap=False,
no_location=False, no_obsolete=False): no_location=False, no_obsolete=False, stdout=sys.stdout):
""" """
Uses the ``locale/`` directory from the Django SVN tree or an Uses the ``locale/`` directory from the Django SVN tree or an
application/project to process all files with translatable literals for application/project to process all files with translatable literals for
@ -312,7 +322,7 @@ def make_messages(locale=None, domain='django', verbosity=1, all=False,
for locale in locales: for locale in locales:
if verbosity > 0: if verbosity > 0:
print "processing language", locale stdout.write("processing language %s" % locale)
basedir = os.path.join(localedir, locale, 'LC_MESSAGES') basedir = os.path.join(localedir, locale, 'LC_MESSAGES')
if not os.path.isdir(basedir): if not os.path.isdir(basedir):
os.makedirs(basedir) os.makedirs(basedir)
@ -324,12 +334,12 @@ def make_messages(locale=None, domain='django', verbosity=1, all=False,
os.unlink(potfile) os.unlink(potfile)
for dirpath, file in find_files(".", ignore_patterns, verbosity, for dirpath, file in find_files(".", ignore_patterns, verbosity,
symlinks=symlinks): stdout, symlinks=symlinks):
process_file(file, dirpath, potfile, domain, verbosity, extensions, process_file(file, dirpath, potfile, domain, verbosity, extensions,
wrap, location) wrap, location, stdout)
if os.path.exists(potfile): if os.path.exists(potfile):
write_po_file(pofile, potfile, domain, locale, verbosity, write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
not invoked_for_django, wrap, location, no_obsolete) not invoked_for_django, wrap, location, no_obsolete)
@ -357,7 +367,7 @@ class Command(NoArgsCommand):
make_option('--no-obsolete', action='store_true', dest='no_obsolete', make_option('--no-obsolete', action='store_true', dest='no_obsolete',
default=False, help="Remove obsolete message strings"), default=False, help="Remove obsolete message strings"),
) )
help = ( "Runs over the entire source tree of the current directory and " help = ("Runs over the entire source tree of the current directory and "
"pulls out all strings marked for translation. It creates (or updates) a message " "pulls out all strings marked for translation. It creates (or updates) a message "
"file in the conf/locale (in the django tree) or locale (for projects and " "file in the conf/locale (in the django tree) or locale (for projects and "
"applications) directory.\n\nYou must run this command with one of either the " "applications) directory.\n\nYou must run this command with one of either the "
@ -386,7 +396,8 @@ class Command(NoArgsCommand):
extensions = handle_extensions(extensions or ['html', 'txt']) extensions = handle_extensions(extensions or ['html', 'txt'])
if verbosity > 1: if verbosity > 1:
sys.stdout.write('examining files with the extensions: %s\n' self.stdout.write('examining files with the extensions: %s\n'
% get_text_list(list(extensions), 'and')) % get_text_list(list(extensions), 'and'))
make_messages(locale, domain, verbosity, process_all, extensions, symlinks, ignore_patterns, no_wrap, no_location, no_obsolete) make_messages(locale, domain, verbosity, process_all, extensions,
symlinks, ignore_patterns, no_wrap, no_location, no_obsolete, self.stdout)

View File

@ -4,6 +4,7 @@ from __future__ import with_statement
import os import os
import re import re
import shutil import shutil
from StringIO import StringIO
from django.core import management from django.core import management
from django.test import TestCase from django.test import TestCase
@ -177,7 +178,11 @@ class IgnoredExtractorTests(ExtractorTests):
def test_ignore_option(self): def test_ignore_option(self):
os.chdir(self.test_dir) os.chdir(self.test_dir)
pattern1 = os.path.join('ignore_dir', '*') pattern1 = os.path.join('ignore_dir', '*')
management.call_command('makemessages', locale=LOCALE, verbosity=0, ignore_patterns=[pattern1]) stdout = StringIO()
management.call_command('makemessages', locale=LOCALE, verbosity=2,
ignore_patterns=[pattern1], stdout=stdout)
data = stdout.getvalue()
self.assertTrue("ignoring directory ignore_dir" in data)
self.assertTrue(os.path.exists(self.PO_FILE)) self.assertTrue(os.path.exists(self.PO_FILE))
with open(self.PO_FILE, 'r') as fp: with open(self.PO_FILE, 'r') as fp:
po_contents = fp.read() po_contents = fp.read()