Skip to content

Commit 2c99fbc

Browse files
michalpokusasarahboyce
authored andcommitted
Fixed #36368 -- Prevented duplicate locale paths and write_po_file calls in makemessages.
1 parent 80cc999 commit 2c99fbc

4 files changed

Lines changed: 89 additions & 6 deletions

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ answer newbie questions, and generally made Django that much better:
743743
Michal Chruszcz <troll@pld-linux.org>
744744
michal@plovarna.cz
745745
Michał Modzelewski <michal.modzelewski@gmail.com>
746+
Michał Pokusa <https://github.com/michalpokusa>
746747
Mihai Damian <yang_damian@yahoo.com>
747748
Mihai Preda <mihai_preda@yahoo.com>
748749
Mikaël Barbero <mikael.barbero nospam at nospam free.fr>

django/core/management/commands/makemessages.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -382,10 +382,15 @@ def handle(self, *args, **options):
382382
self.invoked_for_django = True
383383
else:
384384
if self.settings_available:
385-
self.locale_paths.extend(settings.LOCALE_PATHS)
385+
for path in settings.LOCALE_PATHS:
386+
locale_path = os.path.abspath(path)
387+
if locale_path not in self.locale_paths:
388+
self.locale_paths.append(locale_path)
386389
# Allow to run makemessages inside an app dir
387390
if os.path.isdir("locale"):
388-
self.locale_paths.append(os.path.abspath("locale"))
391+
locale_path = os.path.abspath("locale")
392+
if locale_path not in self.locale_paths:
393+
self.locale_paths.append(locale_path)
389394
if self.locale_paths:
390395
self.default_locale_path = self.locale_paths[0]
391396
os.makedirs(self.default_locale_path, exist_ok=True)
@@ -551,9 +556,10 @@ def find_files(self, root):
551556
self.stdout.write("ignoring directory %s" % dirname)
552557
elif dirname == "locale":
553558
dirnames.remove(dirname)
554-
self.locale_paths.insert(
555-
0, os.path.join(os.path.abspath(dirpath), dirname)
556-
)
559+
locale_dir = os.path.join(os.path.abspath(dirpath), dirname)
560+
if locale_dir in self.locale_paths:
561+
self.locale_paths.remove(locale_dir)
562+
self.locale_paths.insert(0, locale_dir)
557563
for filename in filenames:
558564
file_path = os.path.normpath(os.path.join(dirpath, filename))
559565
file_ext = os.path.splitext(filename)[1]

tests/i18n/commands/app_with_locale/some_file.py

Whitespace-only changes.

tests/i18n/test_extraction.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
from django.core.management import execute_from_command_line
1515
from django.core.management.base import CommandError
1616
from django.core.management.commands.makemessages import Command as MakeMessagesCommand
17-
from django.core.management.commands.makemessages import write_pot_file
17+
from django.core.management.commands.makemessages import (
18+
TranslatableFile,
19+
write_pot_file,
20+
)
1821
from django.core.management.utils import find_command
1922
from django.test import SimpleTestCase, override_settings
2023
from django.test.utils import captured_stderr, captured_stdout
@@ -600,6 +603,79 @@ def test_pot_charset_header_is_utf8(self):
600603
self.assertIn("Content-Type: text/plain; charset=UTF-8", pot_contents)
601604
self.assertIn("mañana; charset=CHARSET", pot_contents)
602605

606+
def test_no_duplicate_locale_paths(self):
607+
for locale_paths in [
608+
[],
609+
[os.path.join(self.test_dir, "locale")],
610+
[Path(self.test_dir, "locale")],
611+
]:
612+
with self.subTest(locale_paths=locale_paths):
613+
with override_settings(LOCALE_PATHS=locale_paths):
614+
cmd = MakeMessagesCommand()
615+
management.call_command(cmd, locale=["en", "ru"], verbosity=0)
616+
self.assertTrue(
617+
all(isinstance(path, str) for path in cmd.locale_paths)
618+
)
619+
self.assertEqual(len(cmd.locale_paths), len(set(cmd.locale_paths)))
620+
621+
def test_no_duplicate_write_po_file_calls(self):
622+
with mock.patch.object(
623+
MakeMessagesCommand, "write_po_file"
624+
) as mock_write_po_file:
625+
cmd = MakeMessagesCommand()
626+
management.call_command(cmd, locale=["en", "ru"], verbosity=0)
627+
self.assertEqual(
628+
len(mock_write_po_file.call_args_list),
629+
len({call.args for call in mock_write_po_file.call_args_list}),
630+
)
631+
632+
def test_correct_translatable_file_locale_dir(self):
633+
634+
class ReturnTrackingMock(mock.Mock):
635+
def __init__(self, *args, **kwargs):
636+
super().__init__(*args, **kwargs)
637+
self.call_return_value_list = []
638+
639+
def __call__(self, *args, **kwargs):
640+
value = super().__call__(*args, **kwargs)
641+
self.call_return_value_list.append(value)
642+
return value
643+
644+
for locale_paths in [
645+
[],
646+
[
647+
os.path.join(self.test_dir, "app_with_locale", "locale"),
648+
],
649+
[
650+
os.path.join(self.test_dir, "locale"),
651+
os.path.join(self.test_dir, "app_with_locale", "locale"),
652+
],
653+
]:
654+
with self.subTest(locale_paths=locale_paths):
655+
with override_settings(LOCALE_PATHS=locale_paths):
656+
cmd = MakeMessagesCommand()
657+
rtm = ReturnTrackingMock(wraps=cmd.find_files)
658+
659+
with mock.patch.object(cmd, "find_files", new=rtm):
660+
management.call_command(cmd, locale=["en", "ru"], verbosity=0)
661+
self.assertEqual(len(rtm.call_args_list), 1)
662+
self.assertEqual(len(rtm.call_return_value_list), 1)
663+
664+
for tf in rtm.call_return_value_list[0]:
665+
self.assertIsInstance(tf, TranslatableFile)
666+
abs_file_path = os.path.abspath(
667+
os.path.join(self.test_dir, tf.dirpath, tf.file)
668+
)
669+
max_common_path = max(
670+
[
671+
os.path.commonpath([abs_file_path, locale_path])
672+
for locale_path in cmd.locale_paths
673+
],
674+
key=len,
675+
)
676+
correct_locale_dir = os.path.join(max_common_path, "locale")
677+
self.assertEqual(tf.locale_dir, correct_locale_dir)
678+
603679

604680
class JavaScriptExtractorTests(ExtractorTests):
605681
PO_FILE = "locale/%s/LC_MESSAGES/djangojs.po" % LOCALE

0 commit comments

Comments
 (0)