瀏覽代碼

Fixed #36010 -- Avoided touching mo files while checking writability.

Claude Paroz 3 月之前
父節點
當前提交
2c47207b3c
共有 2 個文件被更改,包括 12 次插入10 次删除
  1. 5 6
      django/core/management/commands/compilemessages.py
  2. 7 4
      tests/i18n/test_compilation.py

+ 5 - 6
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

+ 7 - 4
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).