maya-umbrella 0.4.1__py2.py3-none-any.whl → 0.6.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of maya-umbrella might be problematic. Click here for more details.
- maya_umbrella/__init__.py +6 -2
- maya_umbrella/__version__.py +1 -1
- maya_umbrella/cleaner.py +111 -0
- maya_umbrella/collector.py +411 -0
- maya_umbrella/constants.py +17 -0
- maya_umbrella/defender.py +161 -0
- maya_umbrella/filesystem.py +151 -26
- maya_umbrella/hooks/delete_turtle.py +3 -3
- maya_umbrella/hooks/delete_unknown_plugin_node.py +4 -3
- maya_umbrella/hooks/fix_model_panel.py +2 -2
- maya_umbrella/hooks/fix_on_model_change_3dc.py +3 -3
- maya_umbrella/i18n.py +76 -0
- maya_umbrella/locales/en_US.json +19 -0
- maya_umbrella/locales/zh_CN.json +19 -0
- maya_umbrella/maya_funs.py +136 -0
- maya_umbrella/scanner.py +101 -0
- maya_umbrella/signatures.py +10 -0
- maya_umbrella/vaccine.py +18 -203
- maya_umbrella/vaccines/vaccine1.py +3 -2
- maya_umbrella/vaccines/vaccine2.py +23 -31
- maya_umbrella/vaccines/vaccine3.py +38 -36
- {maya_umbrella-0.4.1.dist-info → maya_umbrella-0.6.0.dist-info}/METADATA +75 -6
- maya_umbrella-0.6.0.dist-info/RECORD +28 -0
- maya_umbrella/core.py +0 -117
- maya_umbrella-0.4.1.dist-info/RECORD +0 -20
- {maya_umbrella-0.4.1.dist-info → maya_umbrella-0.6.0.dist-info}/LICENSE +0 -0
- {maya_umbrella-0.4.1.dist-info → maya_umbrella-0.6.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Import built-in modules
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
# Import local modules
|
|
6
|
+
from maya_umbrella.cleaner import MayaVirusCleaner
|
|
7
|
+
from maya_umbrella.collector import MayaVirusCollector
|
|
8
|
+
from maya_umbrella.filesystem import get_hooks
|
|
9
|
+
from maya_umbrella.filesystem import load_hook
|
|
10
|
+
from maya_umbrella.i18n import Translator
|
|
11
|
+
from maya_umbrella.log import setup_logger
|
|
12
|
+
from maya_umbrella.maya_funs import is_maya_standalone
|
|
13
|
+
from maya_umbrella.maya_funs import om
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Global list to store IDs of Maya callbacks
|
|
17
|
+
MAYA_UMBRELLA_CALLBACK_IDS = []
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _add_callbacks_id(id_):
|
|
21
|
+
"""Add a callback ID to the global list if it's not already present.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
id_ (int): ID of the callback to be added.
|
|
25
|
+
"""
|
|
26
|
+
global MAYA_UMBRELLA_CALLBACK_IDS
|
|
27
|
+
if id_ not in MAYA_UMBRELLA_CALLBACK_IDS:
|
|
28
|
+
MAYA_UMBRELLA_CALLBACK_IDS.append(id_)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MayaVirusDefender(object):
|
|
32
|
+
"""A class to defend against Maya viruses.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
_vaccines (list): List to store vaccines.
|
|
36
|
+
callback_maps (dict): Dictionary to map callback names to MSceneMessage constants.
|
|
37
|
+
auto_fix (bool): Whether to automatically fix issues.
|
|
38
|
+
logger (Logger): Logger object for logging purposes.
|
|
39
|
+
translator (Translator): Translator object for translation purposes.
|
|
40
|
+
collector (MayaVirusCollector): MayaVirusCollector object for collecting issues.
|
|
41
|
+
virus_cleaner (MayaVirusCleaner): MayaVirusCleaner object for fixing issues.
|
|
42
|
+
hooks (list): List of hooks to run.
|
|
43
|
+
"""
|
|
44
|
+
_vaccines = []
|
|
45
|
+
callback_maps = {
|
|
46
|
+
"after_open": om.MSceneMessage.kAfterOpen,
|
|
47
|
+
"maya_initialized": om.MSceneMessage.kMayaInitialized,
|
|
48
|
+
"after_import": om.MSceneMessage.kAfterImport,
|
|
49
|
+
"after_import_reference": om.MSceneMessage.kAfterImportReference,
|
|
50
|
+
"after_load_reference": om.MSceneMessage.kAfterLoadReference,
|
|
51
|
+
"before_save": om.MSceneMessage.kBeforeSave,
|
|
52
|
+
"before_import": om.MSceneMessage.kBeforeImport,
|
|
53
|
+
"before_load_reference": om.MSceneMessage.kBeforeLoadReference,
|
|
54
|
+
"before_import_reference": om.MSceneMessage.kBeforeImportReference,
|
|
55
|
+
"maya_exiting": om.MSceneMessage.kMayaExiting,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
def __init__(self, auto_fix=True):
|
|
59
|
+
"""Initialize the MayaVirusDefender.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
auto_fix (bool): Whether to automatically fix issues.
|
|
63
|
+
"""
|
|
64
|
+
logger = logging.getLogger(__name__)
|
|
65
|
+
self.auto_fix = auto_fix
|
|
66
|
+
self.logger = setup_logger(logger)
|
|
67
|
+
self.translator = Translator()
|
|
68
|
+
self.collector = MayaVirusCollector(self.logger, self.translator)
|
|
69
|
+
self.virus_cleaner = MayaVirusCleaner(self.collector, self.logger)
|
|
70
|
+
self.hooks = get_hooks()
|
|
71
|
+
|
|
72
|
+
def run_hooks(self):
|
|
73
|
+
"""Run all hooks, only works in non-batch mode."""
|
|
74
|
+
if not is_maya_standalone():
|
|
75
|
+
for hook_file in self.hooks:
|
|
76
|
+
self.logger.debug("run_hook: %s", hook_file)
|
|
77
|
+
try:
|
|
78
|
+
load_hook(hook_file).hook(virus_cleaner=self.virus_cleaner)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
self.logger.debug("Error running hook: %s", e)
|
|
81
|
+
|
|
82
|
+
def collect(self):
|
|
83
|
+
"""Collect all issues related to the Maya virus."""
|
|
84
|
+
self.collector.collect()
|
|
85
|
+
|
|
86
|
+
def fix(self):
|
|
87
|
+
"""Fix all issues related to the Maya virus."""
|
|
88
|
+
self.virus_cleaner.fix()
|
|
89
|
+
|
|
90
|
+
def report(self):
|
|
91
|
+
"""Report all issues related to the Maya virus."""
|
|
92
|
+
self.collect()
|
|
93
|
+
self.collector.report()
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def have_issues(self):
|
|
97
|
+
"""Check if any issues are found.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
bool: True if any issues are found, False otherwise.
|
|
101
|
+
"""
|
|
102
|
+
return self.collector.have_issues
|
|
103
|
+
|
|
104
|
+
def setup(self):
|
|
105
|
+
"""Set up the MayaVirusDefender."""
|
|
106
|
+
self.virus_cleaner.setup_default_callbacks()
|
|
107
|
+
for name, callbacks in self.collector.registered_callbacks.items():
|
|
108
|
+
maya_callback = self.callback_maps[name]
|
|
109
|
+
self.logger.debug("%s setup.", name)
|
|
110
|
+
for func in callbacks:
|
|
111
|
+
_add_callbacks_id(om.MSceneMessage.addCallback(maya_callback, func))
|
|
112
|
+
for name, callbacks in self.callback_maps.items():
|
|
113
|
+
self.logger.debug("setup callback %s.", name)
|
|
114
|
+
_add_callbacks_id(om.MSceneMessage.addCallback(callbacks, self._callback))
|
|
115
|
+
|
|
116
|
+
def stop(self):
|
|
117
|
+
"""Stop the MayaVirusDefender."""
|
|
118
|
+
for ids in MAYA_UMBRELLA_CALLBACK_IDS:
|
|
119
|
+
self.logger.debug("remove callback. %s", ids)
|
|
120
|
+
om.MSceneMessage.removeCallback(ids)
|
|
121
|
+
MAYA_UMBRELLA_CALLBACK_IDS.remove(ids)
|
|
122
|
+
|
|
123
|
+
def get_unfixed_references(self):
|
|
124
|
+
"""Get the list of unfixed reference files.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
list: List of unfixed reference files.
|
|
128
|
+
"""
|
|
129
|
+
self.collect()
|
|
130
|
+
return self.collector.infected_reference_files
|
|
131
|
+
|
|
132
|
+
def _callback(self, *args, **kwargs):
|
|
133
|
+
"""Callback function for MayaVirusDefender.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
*args: Variable length argument list.
|
|
137
|
+
**kwargs: Arbitrary keyword arguments.
|
|
138
|
+
"""
|
|
139
|
+
if self.auto_fix:
|
|
140
|
+
self.collect()
|
|
141
|
+
self.fix()
|
|
142
|
+
self.run_hooks()
|
|
143
|
+
else:
|
|
144
|
+
self.report()
|
|
145
|
+
|
|
146
|
+
def start(self):
|
|
147
|
+
"""Start the MayaVirusDefender."""
|
|
148
|
+
self._callback()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@contextmanager
|
|
152
|
+
def context_defender():
|
|
153
|
+
"""Context manager for MayaVirusDefender.
|
|
154
|
+
|
|
155
|
+
Yields:
|
|
156
|
+
MayaVirusDefender: An instance of MayaVirusDefender.
|
|
157
|
+
"""
|
|
158
|
+
defender = MayaVirusDefender()
|
|
159
|
+
defender.stop()
|
|
160
|
+
yield defender
|
|
161
|
+
defender.setup()
|
maya_umbrella/filesystem.py
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
# Import built-in modules
|
|
2
|
+
from contextlib import contextmanager
|
|
2
3
|
import glob
|
|
3
4
|
import importlib
|
|
4
|
-
import
|
|
5
|
+
import json
|
|
5
6
|
import os
|
|
6
7
|
import random
|
|
8
|
+
import re
|
|
7
9
|
import shutil
|
|
8
10
|
import string
|
|
11
|
+
import sys
|
|
9
12
|
import tempfile
|
|
10
13
|
|
|
11
14
|
# Import local modules
|
|
15
|
+
from maya_umbrella.constants import FILE_VIRUS_SIGNATURES
|
|
12
16
|
from maya_umbrella.constants import PACKAGE_NAME
|
|
13
17
|
|
|
14
18
|
|
|
19
|
+
PY2 = sys.version_info[0] == 2
|
|
20
|
+
PY3 = sys.version_info[0] == 3
|
|
21
|
+
|
|
22
|
+
|
|
15
23
|
def this_root():
|
|
16
24
|
"""Return the absolute path of the current file's directory."""
|
|
17
25
|
return os.path.abspath(os.path.dirname(__file__))
|
|
@@ -21,7 +29,7 @@ def safe_remove_file(file_path):
|
|
|
21
29
|
"""Remove the file at the given path without raising an error if the file does not exist."""
|
|
22
30
|
try:
|
|
23
31
|
os.remove(file_path)
|
|
24
|
-
except OSError:
|
|
32
|
+
except (OSError, IOError): # noqa: UP024
|
|
25
33
|
pass
|
|
26
34
|
|
|
27
35
|
|
|
@@ -29,42 +37,65 @@ def safe_rmtree(path):
|
|
|
29
37
|
"""Remove the directory at the given path without raising an error if the directory does not exist."""
|
|
30
38
|
try:
|
|
31
39
|
shutil.rmtree(path)
|
|
32
|
-
except OSError:
|
|
40
|
+
except (OSError, IOError): # noqa: UP024
|
|
33
41
|
pass
|
|
34
42
|
|
|
35
43
|
|
|
36
44
|
def read_file(path):
|
|
37
45
|
"""Read the content of the file at the given path."""
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
options = {"encoding": "utf-8"} if PY3 else {}
|
|
47
|
+
with open(path, **options) as file_:
|
|
48
|
+
try:
|
|
49
|
+
content = file_.read()
|
|
50
|
+
# maya-2022 UnicodeDecodeError from `plug-ins/mayaHIK.pres.mel`
|
|
51
|
+
except UnicodeDecodeError:
|
|
52
|
+
return ""
|
|
53
|
+
return content
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_json(path):
|
|
57
|
+
"""Read the content of the file at the given path."""
|
|
58
|
+
options = {"encoding": "utf-8"} if PY3 else {}
|
|
59
|
+
with open(path, **options) as file_:
|
|
60
|
+
try:
|
|
61
|
+
content = json.load(file_)
|
|
62
|
+
except UnicodeDecodeError:
|
|
63
|
+
return {}
|
|
40
64
|
return content
|
|
41
65
|
|
|
42
66
|
|
|
43
67
|
def write_file(path, content):
|
|
44
68
|
"""Write the given content to the file at the given path."""
|
|
45
|
-
|
|
69
|
+
options = {"encoding": "utf-8"} if PY3 else {}
|
|
70
|
+
with atomic_writes(path, "w", **options) as file_:
|
|
46
71
|
file_.write(content)
|
|
47
72
|
|
|
48
73
|
|
|
49
|
-
|
|
50
|
-
|
|
74
|
+
@contextmanager
|
|
75
|
+
def atomic_writes(src, mode, **options):
|
|
76
|
+
"""Context manager for atomic writes to a file.
|
|
51
77
|
|
|
52
|
-
|
|
78
|
+
This context manager ensures that the file is only written to disk if the write operation completes without errors.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
src (str): Path to the file to be written.
|
|
82
|
+
mode (str): Mode in which the file is opened, like 'r', 'w', 'a', etc.
|
|
83
|
+
**options: Arbitrary keyword arguments that are passed to the built-in open() function.
|
|
84
|
+
|
|
85
|
+
Yields:
|
|
86
|
+
file object: The opened file object.
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
AttributeError: If the os module does not have the 'replace' function (Python 2 compatibility).
|
|
53
90
|
"""
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
write_file(target, file_data)
|
|
63
|
-
|
|
64
|
-
if report_error:
|
|
65
|
-
not_found = list(set(key_values.keys()) - set(found_keys))
|
|
66
|
-
if not_found:
|
|
67
|
-
raise IndexError("Not found: {0}".format(not_found))
|
|
91
|
+
temp_path = os.path.join(os.path.dirname(src), "._{}".format(id_generator()))
|
|
92
|
+
with open(temp_path, mode, **options) as f:
|
|
93
|
+
yield f
|
|
94
|
+
try:
|
|
95
|
+
os.replace(temp_path, src)
|
|
96
|
+
except AttributeError:
|
|
97
|
+
shutil.move(temp_path, src)
|
|
98
|
+
|
|
68
99
|
|
|
69
100
|
|
|
70
101
|
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
|
|
@@ -77,7 +108,7 @@ def rename(src):
|
|
|
77
108
|
dst = os.path.join(os.path.dirname(src), "._{}".format(id_generator()))
|
|
78
109
|
try:
|
|
79
110
|
os.rename(src, dst)
|
|
80
|
-
except OSError:
|
|
111
|
+
except (OSError, IOError): # noqa: UP024
|
|
81
112
|
return src
|
|
82
113
|
return dst
|
|
83
114
|
|
|
@@ -88,7 +119,7 @@ def load_hook(hook_file):
|
|
|
88
119
|
if hasattr(importlib, "machinery"):
|
|
89
120
|
# Python 3
|
|
90
121
|
# Import built-in modules
|
|
91
|
-
from importlib.util import spec_from_loader
|
|
122
|
+
from importlib.util import spec_from_loader # noqa: F401
|
|
92
123
|
|
|
93
124
|
loader = importlib.machinery.SourceFileLoader(hook_name, hook_file)
|
|
94
125
|
spec = importlib.util.spec_from_loader(loader.name, loader=loader)
|
|
@@ -137,7 +168,101 @@ def get_log_file():
|
|
|
137
168
|
root = get_log_root()
|
|
138
169
|
try:
|
|
139
170
|
os.makedirs(root)
|
|
140
|
-
except OSError:
|
|
171
|
+
except (OSError, IOError): # noqa: UP024
|
|
141
172
|
pass
|
|
142
173
|
name = os.getenv("MAYA_UMBRELLA_LOG_NAME", PACKAGE_NAME)
|
|
143
174
|
return os.path.join(root, "{name}.log".format(name=name))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def remove_virus_file_by_signature(file_path, signatures, output_file_path=None):
|
|
178
|
+
"""Remove virus content from a file by matching signatures.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
file_path (str): Path to the file to be cleaned.
|
|
182
|
+
signatures (list): List of signatures to match and remove.
|
|
183
|
+
output_file_path (str, optional): Path to the cleaned output file.
|
|
184
|
+
Defaults to None, which overwrites the input file.
|
|
185
|
+
"""
|
|
186
|
+
data = read_file(file_path)
|
|
187
|
+
if check_virus_by_signature(data, signatures):
|
|
188
|
+
fixed_data = replace_content_by_signatures(data, signatures)
|
|
189
|
+
write_file(output_file_path or file_path, fixed_data)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def replace_content_by_signatures(content, signatures):
|
|
193
|
+
"""Replace content in a string that matches given signatures.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
content (str): The input content.
|
|
197
|
+
signatures (list): List of signatures to match and remove.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
str: The cleaned content.
|
|
201
|
+
"""
|
|
202
|
+
for signature in signatures:
|
|
203
|
+
content = re.sub(signature, "", content)
|
|
204
|
+
return content
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def check_virus_file_by_signature(file_path, signatures=None):
|
|
208
|
+
"""Check if a file contains a virus by matching signatures.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
file_path (str): Path to the file to be checked.
|
|
212
|
+
signatures (list, optional): List of signatures to match. Defaults to None, which uses FILE_VIRUS_SIGNATURES.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
bool: True if a virus signature is found, False otherwise.
|
|
216
|
+
"""
|
|
217
|
+
signatures = signatures or FILE_VIRUS_SIGNATURES
|
|
218
|
+
try:
|
|
219
|
+
data = read_file(file_path)
|
|
220
|
+
return check_virus_by_signature(data, signatures)
|
|
221
|
+
except (OSError, IOError): # noqa: UP024
|
|
222
|
+
return False
|
|
223
|
+
except UnicodeDecodeError:
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def check_virus_by_signature(content, signatures=None):
|
|
228
|
+
"""Check if a content contains a virus by matching signatures.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
content (str): The input content.
|
|
232
|
+
signatures (list, optional): List of signatures to match. Defaults to None, which uses FILE_VIRUS_SIGNATURES.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
bool: True if a virus signature is found, False otherwise.
|
|
236
|
+
"""
|
|
237
|
+
signatures = signatures or FILE_VIRUS_SIGNATURES
|
|
238
|
+
for signature in signatures:
|
|
239
|
+
if re.search(signature, content):
|
|
240
|
+
return True
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def get_backup_path(path, root_path=None):
|
|
245
|
+
"""Get the backup path for a given file path based on environment variables.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
path (str): Path to the original file.
|
|
249
|
+
root_path (str, optional): Path to the root folder where backups should be saved.
|
|
250
|
+
Defaults to None, which saves backups in the original file's folder.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
str: The backup path.
|
|
254
|
+
"""
|
|
255
|
+
ignore_backup = os.getenv("MAYA_UMBRELLA_IGNORE_BACKUP", "false").lower() == "true"
|
|
256
|
+
if ignore_backup:
|
|
257
|
+
return path
|
|
258
|
+
root, filename = os.path.split(path)
|
|
259
|
+
backup_folder_name = os.getenv("MAYA_UMBRELLA_BACKUP_FOLDER_NAME", "_virus")
|
|
260
|
+
backup_path = os.path.join(root, backup_folder_name)
|
|
261
|
+
if root_path:
|
|
262
|
+
_, base_path = os.path.splitdrive(root)
|
|
263
|
+
backup_path = os.path.join(root_path, base_path.strip(os.sep))
|
|
264
|
+
try:
|
|
265
|
+
os.makedirs(backup_path)
|
|
266
|
+
except (OSError, IOError): # noqa: UP024
|
|
267
|
+
pass
|
|
268
|
+
return os.path.join(backup_path, filename)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# Import
|
|
2
|
-
|
|
1
|
+
# Import local modules
|
|
2
|
+
from maya_umbrella.maya_funs import cmds
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def hook(virus_cleaner):
|
|
@@ -16,7 +16,8 @@ def hook(virus_cleaner):
|
|
|
16
16
|
cmds.lockNode(nodeObj, lock=False)
|
|
17
17
|
except Exception:
|
|
18
18
|
virus_cleaner.logger.warning(
|
|
19
|
-
"The node is locked and cannot be unlocked. skip {}".format(nodeObj)
|
|
19
|
+
"The node is locked and cannot be unlocked. skip {}".format(nodeObj)
|
|
20
|
+
)
|
|
20
21
|
continue
|
|
21
22
|
try:
|
|
22
23
|
cmds.delete(nodeObj)
|
maya_umbrella/i18n.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Import built-in modules
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
from string import Template
|
|
5
|
+
|
|
6
|
+
# Import local modules
|
|
7
|
+
from maya_umbrella.filesystem import read_json
|
|
8
|
+
from maya_umbrella.filesystem import this_root
|
|
9
|
+
from maya_umbrella.maya_funs import maya_ui_language
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Translator(object):
|
|
13
|
+
"""A class to handle translations for different locales.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
data (dict): Dictionary containing translation data for different locales.
|
|
17
|
+
locale (str): The current locale.
|
|
18
|
+
"""
|
|
19
|
+
def __init__(self, file_format="json", default_locale=None):
|
|
20
|
+
"""Initialize the Translator.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
file_format (str, optional): File format of the translation files. Defaults to "json".
|
|
24
|
+
default_locale (str, optional): Default locale to use for translations. Defaults to None,
|
|
25
|
+
which uses the MAYA_UMBRELLA_LANG environment variable or the Maya UI language.
|
|
26
|
+
"""
|
|
27
|
+
_default_locale = os.getenv("MAYA_UMBRELLA_LANG", maya_ui_language())
|
|
28
|
+
default_locale = default_locale or _default_locale
|
|
29
|
+
self.data = {}
|
|
30
|
+
self.locale = default_locale
|
|
31
|
+
translations_folder = os.path.join(this_root(), "locales")
|
|
32
|
+
|
|
33
|
+
# get list of files with specific extensions
|
|
34
|
+
files = glob.glob(os.path.join(translations_folder, "*.{file_format}".format(file_format=file_format)))
|
|
35
|
+
for fil in files:
|
|
36
|
+
# get the name of the file without extension, will be used as locale name
|
|
37
|
+
loc = os.path.splitext(os.path.basename(fil))[0]
|
|
38
|
+
self.data[loc] = read_json(fil)
|
|
39
|
+
|
|
40
|
+
def set_locale(self, locale):
|
|
41
|
+
"""Set the current locale.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
locale (str): The locale to set.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
ValueError: If the provided locale is not supported.
|
|
48
|
+
"""
|
|
49
|
+
if locale in self.data:
|
|
50
|
+
self.locale = locale
|
|
51
|
+
else:
|
|
52
|
+
raise ValueError("Invalid locale: {loc}".format(loc=locale))
|
|
53
|
+
|
|
54
|
+
def get_locale(self):
|
|
55
|
+
"""Get the current locale.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
str: The current locale.
|
|
59
|
+
"""
|
|
60
|
+
return self.locale
|
|
61
|
+
|
|
62
|
+
def translate(self, key, **kwargs):
|
|
63
|
+
"""Translate a text based on the current locale.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
key (str): The key to be translated.
|
|
67
|
+
**kwargs: Arbitrary keyword arguments that are used to replace placeholders in the translation text.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
str: The translated text.
|
|
71
|
+
"""
|
|
72
|
+
# return the key instead of translation text if locale is not supported
|
|
73
|
+
if self.locale not in self.data:
|
|
74
|
+
return key
|
|
75
|
+
text = self.data[self.locale].get(key, key)
|
|
76
|
+
return Template(text).safe_substitute(**kwargs)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"start_fix_issues": "Start fixing all problems related to Maya virus $name",
|
|
3
|
+
"finish_fix_issues": "Done.",
|
|
4
|
+
"init_message": "Successfully loaded <hl>maya_umbrella</hl> under protection.",
|
|
5
|
+
"init_standalone_message": "-----------------------Loading maya_umbrella successfully----------------------",
|
|
6
|
+
"report_issue": "$name: Infected by Malware!",
|
|
7
|
+
"infected_nodes": "Infected nodes: $name: ",
|
|
8
|
+
"bad_files": "Bad files: $name",
|
|
9
|
+
"infected_script_jobs": "Infected script jobs: $name",
|
|
10
|
+
"infected_files": "Infected files: $name",
|
|
11
|
+
"infected_reference_files": "Infected reference files: $name",
|
|
12
|
+
"fix_infected_files": "Clean infected files: $name",
|
|
13
|
+
"fix_infected_nodes": "Delete infected nodes:$name",
|
|
14
|
+
"fix_infected_reference_nodes": "trying fix infected nodes from reference:$name",
|
|
15
|
+
"delete": "Deleting: $name",
|
|
16
|
+
"remove_file": "Deleting file:$name",
|
|
17
|
+
"remove_path": "Deleting path:$name",
|
|
18
|
+
"fix_script_job": "Kill script job: %s"
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"start_fix_issues": "开始修复与 Maya 病毒相关的所有问题 $name",
|
|
3
|
+
"finish_fix_issues": "修复结束.",
|
|
4
|
+
"init_message": "成功加载 <hl>maya_umbrella</hl> 保护中.",
|
|
5
|
+
"init_standalone_message": "-----------------------成功加载 maya_umbrella-----------------------",
|
|
6
|
+
"report_issue": "$name:被恶意感染!",
|
|
7
|
+
"infected_nodes": "被感染节点:$name: ",
|
|
8
|
+
"bad_files": "需要被清理的文件:$name",
|
|
9
|
+
"infected_script_jobs": "被感染的script jobs:$name",
|
|
10
|
+
"infected_files": "被感染的脚本:$name",
|
|
11
|
+
"infected_reference_files": "被感染的参考文件:$name",
|
|
12
|
+
"fix_infected_files": "清理被感染的文件:$name",
|
|
13
|
+
"fix_infected_nodes": "删除被感染的节点:$name",
|
|
14
|
+
"fix_infected_reference_nodes": "尝试修复被感染的参考节点:$name",
|
|
15
|
+
"delete": "删除感染文件:$name",
|
|
16
|
+
"remove_file": "删除文件:$name",
|
|
17
|
+
"remove_path": "删除文件夹:$name",
|
|
18
|
+
"fix_script_job": "删除被感染的节点:$name"
|
|
19
|
+
}
|