Coverage for app/venv/lib/python3.14/site-packages/weblate/utils/files.py: 61%
52 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
4from __future__ import annotations
6import os
7import shutil
8import stat
9from typing import TYPE_CHECKING
11from django.conf import settings
12from django.db.models import TextChoices
13from django.utils.translation import gettext, gettext_lazy, ngettext
14from translation_finder.finder import EXCLUDES
16if TYPE_CHECKING: 16 ↛ 17line 16 didn't jump to line 17 because the condition on line 16 was never true
17 from pathlib import Path
19WEBLATE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
20BASE_DIR = os.path.dirname(WEBLATE_DIR)
21DEFAULT_DATA_DIR = os.path.join(BASE_DIR, "data")
22DEFAULT_TEST_DIR = os.path.join(BASE_DIR, "data-test")
23BUILD_DIR = os.path.join(BASE_DIR, "build")
24VENV_DIR = os.path.join(BASE_DIR, ".venv")
25DOCS_DIR = os.path.join(BASE_DIR, "docs")
26SCRIPTS_DIR = os.path.join(BASE_DIR, "scripts")
27CLIENT_DIR = os.path.join(BASE_DIR, "client")
28EXAMPLES_DIR = os.path.join(BASE_DIR, "weblate", "examples")
30PATH_EXCLUDES = [f"/{exclude}/" for exclude in EXCLUDES]
33class FileUploadMethod(TextChoices):
34 TRANSLATE = "translate", gettext_lazy("Add as translation")
35 APPROVE = "approve", gettext_lazy("Add as approved translation")
36 SUGGEST = "suggest", gettext_lazy("Add as suggestion")
37 FUZZY = "fuzzy", gettext_lazy("Add as translation needing edit")
38 REPLACE = "replace", gettext_lazy("Replace existing translation file")
39 SOURCE = "source", gettext_lazy("Update source strings")
40 ADD = "add", gettext_lazy("Add new strings")
43def get_upload_message(not_found: int, skipped: int, accepted: int, total: int) -> str:
44 if total == 0:
45 return gettext("No strings were imported from the uploaded file.")
46 return ngettext(
47 "Processed {0} string from the uploaded files "
48 "(skipped: {1}, not found: {2}, updated: {3}).",
49 "Processed {0} strings from the uploaded files "
50 "(skipped: {1}, not found: {2}, updated: {3}).",
51 total,
52 ).format(total, skipped, not_found, accepted)
55def remove_readonly(func, path, excinfo) -> None:
56 """Clear the readonly bit and reattempt the removal."""
57 if isinstance(excinfo[1], FileNotFoundError):
58 return
59 if os.path.isdir(path):
60 os.chmod(path, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
61 else:
62 os.chmod(path, stat.S_IREAD | stat.S_IWRITE)
63 if func in {os.open, os.lstat, os.rmdir}:
64 # Could not remove a directory
65 remove_tree(path)
66 else:
67 func(path)
70def remove_tree(path: str | Path, ignore_errors: bool = False) -> None:
71 # TODO: switch to onexc with Python >= 3.12
72 shutil.rmtree(path, ignore_errors=ignore_errors, onerror=remove_readonly)
75def should_skip(location):
76 """Check for skipping location in manage commands."""
77 location = os.path.abspath(location)
78 return not location.startswith(WEBLATE_DIR) or location.startswith(
79 (
80 VENV_DIR,
81 settings.DATA_DIR,
82 DEFAULT_DATA_DIR,
83 BUILD_DIR,
84 DEFAULT_TEST_DIR,
85 DOCS_DIR,
86 SCRIPTS_DIR,
87 CLIENT_DIR,
88 EXAMPLES_DIR,
89 )
90 )
93def is_excluded(path):
94 """Whether path should be excluded from zip extraction."""
95 return any(exclude in f"/{path}/" for exclude in PATH_EXCLUDES) or ".." in path
98def cleanup_error_message(text: str) -> str:
99 """Remove absolute paths from the text."""
100 return text.replace(settings.CACHE_DIR, "...").replace(settings.DATA_DIR, "...")