maya-umbrella 0.5.0__py2.py3-none-any.whl → 0.6.1__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.
maya_umbrella/__init__.py CHANGED
@@ -1,5 +1,9 @@
1
1
  # Import local modules
2
- from maya_umbrella.core import MayaVirusDefender
2
+ from maya_umbrella.cleaner import MayaVirusCleaner
3
+ from maya_umbrella.collector import MayaVirusCollector
4
+ from maya_umbrella.defender import MayaVirusDefender
5
+ from maya_umbrella.defender import context_defender
6
+ from maya_umbrella.scanner import MayaVirusScanner
3
7
 
4
8
 
5
- __all__ = ["MayaVirusDefender"]
9
+ __all__ = ["MayaVirusDefender", "MayaVirusCleaner", "MayaVirusCollector", "MayaVirusScanner", "context_defender"]
@@ -1 +1 @@
1
- __version__ = "0.5.0"
1
+ __version__ = "0.6.1"
@@ -0,0 +1,111 @@
1
+ # Import built-in modules
2
+ import glob
3
+ import logging
4
+ import os
5
+ import re
6
+
7
+ # Import local modules
8
+ from maya_umbrella.constants import FILE_VIRUS_SIGNATURES
9
+ from maya_umbrella.filesystem import remove_virus_file_by_signature
10
+ from maya_umbrella.filesystem import safe_remove_file
11
+ from maya_umbrella.filesystem import safe_rmtree
12
+ from maya_umbrella.i18n import Translator
13
+ from maya_umbrella.maya_funs import check_reference_node_exists
14
+ from maya_umbrella.maya_funs import cmds
15
+
16
+
17
+ class MayaVirusCleaner(object):
18
+ """A class to clean Maya virus files.
19
+
20
+ Attributes:
21
+ logger (Logger): Logger object for logging purposes.
22
+ translator (Translator): Translator object for translation purposes.
23
+ collector (MayaVirusCollector): MayaVirusCollector object for collecting issues.
24
+ """
25
+ def __init__(self, collector, logger=None):
26
+ """Initialize the MayaVirusCleaner.
27
+
28
+ Args:
29
+ collector (MayaVirusCollector): MayaVirusCollector object for collecting issues.
30
+ logger (Logger, optional): Logger object for logging purposes. Defaults to None, which creates a new logger.
31
+ """
32
+ self.logger = logger or logging.getLogger(__name__)
33
+ self.translator = Translator()
34
+ self.collector = collector
35
+
36
+ def callback_remove_rename_temp_files(self, *args, **kwargs):
37
+ """Remove temporary files in the local script path."""
38
+ self.logger.debug("Removing temporary files in %s", self.collector.local_script_path)
39
+ for temp_file in glob.glob(os.path.join(self.collector.local_script_path, "._*")):
40
+ safe_remove_file(temp_file)
41
+
42
+ def fix_script_jobs(self):
43
+ """Fix infected script jobs."""
44
+ for script_job in self.collector.infected_script_jobs:
45
+ script_num = int(re.findall(r"^(\d+):", script_job)[0])
46
+ self.logger.debug("Kill script job %s", script_job)
47
+ cmds.scriptJob(kill=script_num, force=True)
48
+ self.collector.remove_infected_script_job(script_job)
49
+
50
+ def fix_malicious_files(self):
51
+ """Fix malicious files."""
52
+ for file_ in self.collector.malicious_files:
53
+ if os.path.exists(file_):
54
+ if os.path.isfile(file_):
55
+ self.logger.debug(self.translator.translate("remove_file", name=file_))
56
+ safe_remove_file(file_)
57
+ self.collector.remove_malicious_file(file_)
58
+ else:
59
+ self.logger.debug(self.translator.translate("remove_path", name=file_))
60
+ safe_rmtree(file_)
61
+ self.collector.remove_malicious_file(file_)
62
+
63
+ def fix_infected_nodes(self):
64
+ """Fix infected nodes."""
65
+ for node in self.collector.infected_nodes:
66
+ is_referenced = check_reference_node_exists(node)
67
+ if is_referenced:
68
+ try:
69
+ self.logger.debug(self.translator.translate("fix_infected_reference_nodes", name=node))
70
+ cmds.setAttr("{node}.before".format(node=node), "", type="string")
71
+ cmds.setAttr("{node}.after".format(node=node), "", type="string")
72
+ cmds.setAttr("{node}.scriptType".format(node=node), 0)
73
+ self.collector.remove_infected_node(node)
74
+ except Exception as e:
75
+ self.logger.debug(e)
76
+ else:
77
+ try:
78
+ cmds.lockNode(node, lock=False)
79
+ except ValueError:
80
+ pass
81
+ try:
82
+ self.logger.debug(self.translator.translate("fix_infected_nodes", name=node))
83
+ cmds.delete(node)
84
+ except ValueError:
85
+ pass
86
+ self.collector.remove_infected_node(node)
87
+
88
+ def setup_default_callbacks(self):
89
+ """Set up default callbacks."""
90
+ self.collector.add_maya_initialized_callback(self.callback_remove_rename_temp_files)
91
+ self.collector.add_maya_exiting_callback(self.callback_remove_rename_temp_files)
92
+
93
+ def fix_infected_files(self):
94
+ """Fix infected files."""
95
+ for file_path in self.collector.infected_files:
96
+ self.logger.info(self.translator.translate("fix_infected_files", name=file_path))
97
+ remove_virus_file_by_signature(file_path, FILE_VIRUS_SIGNATURES)
98
+ self.collector.remove_infected_file(file_path)
99
+
100
+ def fix(self):
101
+ """Fix all issues related to the Maya virus."""
102
+ if self.collector.have_issues:
103
+ maya_file = cmds.file(query=True, sceneName=True, shortName=True) or "empty/scene"
104
+ self.logger.info(self.translator.translate("start_fix_issues", name=maya_file))
105
+ self.fix_malicious_files()
106
+ self.fix_infected_files()
107
+ self.fix_infected_nodes()
108
+ self.fix_script_jobs()
109
+ for func in self.collector.get_additionally_fix_funcs():
110
+ func()
111
+ self.logger.info(self.translator.translate("finish_fix_issues", name=maya_file))
@@ -0,0 +1,411 @@
1
+ # Import built-in modules
2
+ from collections import defaultdict
3
+ import logging
4
+ import os
5
+
6
+ # Import local modules
7
+ from maya_umbrella.filesystem import get_vaccines
8
+ from maya_umbrella.filesystem import load_hook
9
+ from maya_umbrella.i18n import Translator
10
+ from maya_umbrella.maya_funs import cmds
11
+
12
+
13
+ class MayaVirusCollector(object):
14
+ """A class to collect and handle Maya viruses.
15
+
16
+ Attributes:
17
+ _malicious_files (list): List to store malicious files.
18
+ _infected_files (list): List to store infected files.
19
+ _infected_nodes (list): List to store infected nodes.
20
+ _infected_script_nodes (list): List to store infected script nodes.
21
+ _infected_reference_files (list): List to store infected reference files.
22
+ _infected_script_jobs (list): List to store infected script jobs.
23
+ _registered_callbacks (defaultdict): Dictionary to store registered callbacks.
24
+ _additionally_fix_funcs (list): List to store additional fix functions.
25
+ _vaccines (list): List to store vaccines.
26
+ logger: Logger object for logging purposes.
27
+ translator: Translator object for translation purposes.
28
+ """
29
+
30
+ def __init__(self, logger, translator=None):
31
+ """Initialize MayaVirusCollector.
32
+
33
+ Args:
34
+ logger (Logger, optional): Logger object for logging purposes.
35
+ translator (Translator, optional): Translator object for translation purposes.
36
+ """
37
+ self.logger = logger or logging.getLogger(__name__)
38
+ self.translator = translator or Translator()
39
+ # Malicious files or temp files that need to be deleted directly.
40
+ self._malicious_files = []
41
+ self._infected_files = []
42
+ self._infected_nodes = []
43
+ self._infected_script_nodes = []
44
+ self._infected_reference_files = []
45
+ self._infected_script_jobs = []
46
+ self._registered_callbacks = defaultdict(list)
47
+ self._additionally_fix_funcs = []
48
+ self._vaccines = []
49
+ self.load_vaccines()
50
+
51
+ def load_vaccines(self):
52
+ """Load all vaccines."""
53
+ for vaccine in get_vaccines():
54
+ vaccine_class = load_hook(vaccine).Vaccine
55
+ try:
56
+ self._vaccines.append(vaccine_class(api=self, logger=self.logger))
57
+ except Exception as e:
58
+ self.logger.error("Error loading vaccine: %s", e)
59
+
60
+ @property
61
+ def vaccines(self):
62
+ """Get all loaded vaccines.
63
+
64
+ Returns:
65
+ list: A list of loaded vaccines.
66
+ """
67
+ return self._vaccines
68
+
69
+ @property
70
+ def user_app_dir(self):
71
+ """Return the user application directory."""
72
+ return cmds.internalVar(userAppDir=True)
73
+
74
+ @property
75
+ def maya_install_root(self):
76
+ """Return the Maya installation root directory."""
77
+ return os.environ["MAYA_LOCATION"]
78
+
79
+ @property
80
+ def user_script_path(self):
81
+ """Return the user script directory."""
82
+ return cmds.internalVar(userScriptDir=True)
83
+
84
+ @property
85
+ def local_script_path(self):
86
+ """Return the local script directory."""
87
+ return os.path.join(self.user_app_dir, "scripts")
88
+
89
+ @property
90
+ def malicious_files(self):
91
+ """Return a list of bad files."""
92
+ return [path for path in list(set(self._malicious_files)) if os.path.exists(path)]
93
+
94
+ @property
95
+ def infected_nodes(self):
96
+ """Return a list of infected nodes."""
97
+ return list(set(self._infected_nodes))
98
+
99
+ @property
100
+ def infected_reference_files(self):
101
+ return [path for path in list(set(self._infected_reference_files)) if os.path.exists(path)]
102
+
103
+ @property
104
+ def infected_script_nodes(self):
105
+ """Return a list of bad script nodes."""
106
+ return list(set(self._infected_script_nodes))
107
+
108
+ @property
109
+ def infected_script_jobs(self):
110
+ """Return a list of bad script jobs."""
111
+ return list(set(self._infected_script_jobs))
112
+
113
+ @property
114
+ def infected_files(self):
115
+ """Return the list of infected files.
116
+
117
+ Returns:
118
+ list: List of infected files.
119
+ """
120
+ return self._infected_files
121
+
122
+ @property
123
+ def registered_callbacks(self):
124
+ """Return the dictionary of registered callbacks.
125
+
126
+ Returns:
127
+ defaultdict: Dictionary of registered callbacks.
128
+ """
129
+ return self._registered_callbacks
130
+
131
+ def add_infected_reference_files(self, files):
132
+ """Add multiple infected reference files.
133
+
134
+ Args:
135
+ files (list): List of infected reference files to be added.
136
+ """
137
+ self._infected_reference_files.extend(files)
138
+
139
+ def add_infected_reference_file(self, file):
140
+ """Add a single infected reference file.
141
+
142
+ Args:
143
+ file (str): Infected reference file to be added.
144
+ """
145
+ self._infected_reference_files.append(file)
146
+
147
+ def remove_infected_reference_file(self, file):
148
+ """Remove an infected reference file.
149
+
150
+ Args:
151
+ file (str): Infected reference file to be removed.
152
+ """
153
+ self._infected_reference_files.remove(file)
154
+
155
+ def add_infected_files(self, files):
156
+ """Add multiple infected files.
157
+
158
+ Args:
159
+ files (list): List of infected files to be added.
160
+ """
161
+ self._infected_files.extend(files)
162
+
163
+ def add_infected_file(self, file):
164
+ """Add a single infected file.
165
+
166
+ Args:
167
+ file (str): Infected file to be added.
168
+ """
169
+ self._infected_files.append(file)
170
+
171
+ def remove_infected_file(self, file):
172
+ """Remove an infected file.
173
+
174
+ Args:
175
+ file (str): Infected file to be removed.
176
+ """
177
+ self._infected_files.remove(file)
178
+
179
+ def add_malicious_files(self, files):
180
+ """Add multiple malicious files.
181
+
182
+ Args:
183
+ files (list): List of malicious files to be added.
184
+ """
185
+ self._malicious_files.extend(files)
186
+
187
+ def add_malicious_file(self, file):
188
+ """Add a single malicious file.
189
+
190
+ Args:
191
+ file (str): Malicious file to be added.
192
+ """
193
+ self._malicious_files.append(file)
194
+
195
+ def remove_malicious_file(self, file):
196
+ """Remove a malicious file.
197
+
198
+ Args:
199
+ file (str): Malicious file to be removed.
200
+ """
201
+ self._malicious_files.remove(file)
202
+
203
+ def add_infected_nodes(self, nodes):
204
+ """Add multiple infected nodes.
205
+
206
+ Args:
207
+ nodes (list): List of infected nodes to be added.
208
+ """
209
+ self._infected_nodes.extend(nodes)
210
+
211
+ def add_infected_node(self, node):
212
+ """Add a single infected node.
213
+
214
+ Args:
215
+ node (str): Infected node to be added.
216
+ """
217
+ self._infected_nodes.append(node)
218
+
219
+ def remove_infected_node(self, node):
220
+ """Remove an infected node.
221
+
222
+ Args:
223
+ node (str): Infected node to be removed.
224
+ """
225
+ self._infected_nodes.remove(node)
226
+
227
+ def add_infected_script_jobs(self, jobs):
228
+ """Add multiple infected script jobs.
229
+
230
+ Args:
231
+ jobs (list): List of infected script jobs to be added.
232
+ """
233
+ self._infected_script_jobs.extend(jobs)
234
+
235
+ def add_infected_script_job(self, job):
236
+ """Add a single infected script job.
237
+
238
+ Args:
239
+ job (str): Infected script job to be added.
240
+ """
241
+ self._infected_script_jobs.append(job)
242
+
243
+ def remove_infected_script_job(self, job):
244
+ """Remove an infected script job.
245
+
246
+ Args:
247
+ job (str): Infected script job to be removed.
248
+ """
249
+ self._infected_script_jobs.remove(job)
250
+
251
+ def add_infected_script_nodes(self, nodes):
252
+ """Add multiple infected script nodes.
253
+
254
+ Args:
255
+ nodes (list): List of infected script nodes to be added.
256
+ """
257
+ self._infected_script_nodes.extend(nodes)
258
+
259
+ def add_infected_script_node(self, node):
260
+ """Add a single infected script node.
261
+
262
+ Args:
263
+ node (str): Infected script node to be added.
264
+ """
265
+ self._infected_script_nodes.append(node)
266
+
267
+ def remove_infected_script_node(self, node):
268
+ """Remove an infected script node.
269
+
270
+ Args:
271
+ node (str): Infected script node to be removed.
272
+ """
273
+ self._infected_script_nodes.remove(node)
274
+
275
+ def get_additionally_fix_funcs(self):
276
+ """Return the list of additional fix functions.
277
+
278
+ Returns:
279
+ list: List of additional fix functions.
280
+ """
281
+ return self._additionally_fix_funcs
282
+
283
+ def register_callback(self, callback_name, callback):
284
+ """Register a callback to be executed before or after processing."""
285
+ self._registered_callbacks[callback_name].append(callback)
286
+
287
+ def add_after_open_callback(self, callback):
288
+ """Add a callback to be executed after a file is opened.
289
+
290
+ Args:
291
+ callback (function): The callback function to be added.
292
+ """
293
+ self.register_callback("after_open", callback)
294
+
295
+ def add_maya_initialized_callback(self, callback):
296
+ """Add a callback to be executed when Maya is initialized.
297
+
298
+ Args:
299
+ callback (function): The callback function to be added.
300
+ """
301
+ self.register_callback("maya_initialized", callback)
302
+
303
+ def add_after_import_callback(self, callback):
304
+ """Add a callback to be executed after a file is imported.
305
+
306
+ Args:
307
+ callback (function): The callback function to be added.
308
+ """
309
+ self.register_callback("after_import", callback)
310
+
311
+ def add_after_import_reference_callback(self, callback):
312
+ """Add a callback to be executed after a reference file is imported.
313
+
314
+ Args:
315
+ callback (function): The callback function to be added.
316
+ """
317
+ self.register_callback("after_import_reference", callback)
318
+
319
+ def add_after_load_reference_callback(self, callback):
320
+ """Add a callback to be executed after a reference file is loaded.
321
+
322
+ Args:
323
+ callback (function): The callback function to be added.
324
+ """
325
+ self.register_callback("after_load_reference", callback)
326
+
327
+ def add_before_save_callback(self, callback):
328
+ """Add a callback to be executed before a file is saved.
329
+
330
+ Args:
331
+ callback (function): The callback function to be added.
332
+ """
333
+ self.register_callback("before_save", callback)
334
+
335
+ def add_before_import_callback(self, callback):
336
+ """Add a callback to be executed before a file is imported.
337
+
338
+ Args:
339
+ callback (function): The callback function to be added.
340
+ """
341
+ self.register_callback("before_import", callback)
342
+
343
+ def add_before_load_reference_callback(self, callback):
344
+ """Add a callback to be executed before a reference file is loaded.
345
+
346
+ Args:
347
+ callback (function): The callback function to be added.
348
+ """
349
+ self.register_callback("before_load_reference", callback)
350
+
351
+ def add_before_import_reference_callback(self, callback):
352
+ """Add a callback to be executed before a reference file is imported.
353
+
354
+ Args:
355
+ callback (function): The callback function to be added.
356
+ """
357
+ self.register_callback("before_import_reference", callback)
358
+
359
+ def add_maya_exiting_callback(self, callback):
360
+ """Add a callback to be executed when Maya is exiting.
361
+
362
+ Args:
363
+ callback (function): The callback function to be added.
364
+ """
365
+ self.register_callback("maya_exiting", callback)
366
+
367
+ def add_additionally_fix_function(self, func):
368
+ """Add an additional fix function to be executed.
369
+
370
+ Args:
371
+ func (function): The fix function to be added.
372
+ """
373
+ self._additionally_fix_funcs.append(func)
374
+
375
+ def collect(self):
376
+ """Collect issues from all loaded vaccines."""
377
+ self.reset()
378
+ for vaccine in self.vaccines:
379
+ vaccine.collect_issues()
380
+
381
+ @property
382
+ def have_issues(self):
383
+ """Check if any issues are found.
384
+
385
+ Returns:
386
+ bool: True if any issues are found, False otherwise.
387
+ """
388
+ return any([self.malicious_files, self.infected_nodes, self.infected_script_nodes, self.infected_files,
389
+ self.infected_script_jobs])
390
+
391
+ def report(self):
392
+ """Report all issues related to the Maya virus."""
393
+ for name in (
394
+ "malicious_files",
395
+ "infected_nodes",
396
+ "infected_script_jobs",
397
+ "infected_files",
398
+ "infected_reference_files"
399
+ ):
400
+ self.logger.info(self.translator.translate(name, name=getattr(self, name)))
401
+
402
+ def reset(self):
403
+ """Reset all issues related to the Maya virus."""
404
+ self._malicious_files = []
405
+ self._infected_nodes = []
406
+ self._infected_script_nodes = []
407
+ self._infected_script_jobs = []
408
+ self._infected_files = []
409
+ self._infected_reference_files = []
410
+ self._registered_callbacks = defaultdict(list)
411
+ self._additionally_fix_funcs = []
@@ -7,6 +7,7 @@ LOG_MAX_BYTES = 1024 * 1024 * 5
7
7
  FILE_VIRUS_SIGNATURES = [
8
8
  "import vaccine",
9
9
  "cmds.evalDeferred.*leukocyte.+",
10
+ # https://regex101.com/r/0MNzF7/1
10
11
  "python(.*);.+exec.+(pyCode).+;",
11
12
  ]
12
13
 
@@ -14,5 +15,8 @@ JOB_SCRIPTS_VIRUS_SIGNATURES = [
14
15
  "petri_dish_path.+cmds.internalVar.+",
15
16
  "userSetup",
16
17
  "fuckVirus",
18
+ # https://regex101.com/r/0MNzF7/1
17
19
  "python(.*);.+exec.+(pyCode).+;",
20
+ # https://regex101.com/r/2D14UA/1
21
+ r"^\['.+']",
18
22
  ]