summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2024-12-20 23:26:22 +0100
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2025-01-23 11:37:48 +0100
commit72c0359dda80df22d0e922874ad0377e20f90be7 (patch)
treeca9d319b48b2aa0f14631140e0c77fcabd60c024
parent698d05c11c27d4ed5fd75194ac0edcf133bd7600 (diff)
[5.2.x] Fixed #36010 -- Avoided touching mo files while checking writability.
Backport of 2c47207b3c8412d16e61e388f176b47b41b40794 from main.
-rw-r--r--django/core/management/commands/compilemessages.py11
-rw-r--r--tests/i18n/test_compilation.py11
2 files changed, 12 insertions, 10 deletions
diff --git a/django/core/management/commands/compilemessages.py b/django/core/management/commands/compilemessages.py
index c56e2a237c..0aaff5822a 100644
--- a/django/core/management/commands/compilemessages.py
+++ b/django/core/management/commands/compilemessages.py
@@ -2,6 +2,7 @@ import codecs
import concurrent.futures
import glob
import os
+import tempfile
from pathlib import Path
from django.core.management.base import BaseCommand, CommandError
@@ -16,12 +17,10 @@ def has_bom(fn):
)
-def is_writable(path):
- # Known side effect: updating file access/modified time to current time if
- # it is writable.
+def is_dir_writable(path):
try:
- with open(path, "a"):
- os.utime(path, None)
+ with tempfile.NamedTemporaryFile(dir=path):
+ pass
except OSError:
return False
return True
@@ -172,7 +171,7 @@ class Command(BaseCommand):
continue
# Check writability on first location
- if i == 0 and not is_writable(mo_path):
+ if i == 0 and not is_dir_writable(mo_path.parent):
self.stderr.write(
"The po files under %s are in a seemingly not writable "
"location. mo files will not be updated/created." % dirpath
diff --git a/tests/i18n/test_compilation.py b/tests/i18n/test_compilation.py
index 7b02776dbe..4b0bb9f6bb 100644
--- a/tests/i18n/test_compilation.py
+++ b/tests/i18n/test_compilation.py
@@ -43,9 +43,9 @@ class PoFileTests(MessageCompilationTests):
def test_no_write_access(self):
mo_file_en = Path(self.MO_FILE_EN)
err_buffer = StringIO()
- # Put file in read-only mode.
- old_mode = mo_file_en.stat().st_mode
- mo_file_en.chmod(stat.S_IREAD)
+ # Put parent directory in read-only mode.
+ old_mode = mo_file_en.parent.stat().st_mode
+ mo_file_en.parent.chmod(stat.S_IRUSR | stat.S_IXUSR)
# Ensure .po file is more recent than .mo file.
mo_file_en.with_suffix(".po").touch()
try:
@@ -57,7 +57,7 @@ class PoFileTests(MessageCompilationTests):
)
self.assertIn("not writable location", err_buffer.getvalue())
finally:
- mo_file_en.chmod(old_mode)
+ mo_file_en.parent.chmod(old_mode)
def test_no_compile_when_unneeded(self):
mo_file_en = Path(self.MO_FILE_EN)
@@ -258,6 +258,9 @@ class CompilationErrorHandling(MessageCompilationTests):
# po file contains wrong po formatting.
with self.assertRaises(CommandError):
call_command("compilemessages", locale=["ja"], verbosity=0)
+ # It should still fail a second time.
+ with self.assertRaises(CommandError):
+ call_command("compilemessages", locale=["ja"], verbosity=0)
def test_msgfmt_error_including_non_ascii(self):
# po file contains invalid msgstr content (triggers non-ascii error content).