maya-umbrella 0.3.0__py2.py3-none-any.whl → 0.4.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.

Potentially problematic release.


This version of maya-umbrella might be problematic. Click here for more details.

maya_umbrella/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # Import local modules
2
- from maya_umbrella.core import Defender
2
+ from maya_umbrella.core import MayaVirusDefender
3
3
 
4
4
 
5
- __all__ = ["Defender"]
5
+ __all__ = ["MayaVirusDefender"]
@@ -1 +1 @@
1
- __version__ = "0.3.0"
1
+ __version__ = "0.4.1"
@@ -0,0 +1,5 @@
1
+ PACKAGE_NAME = "maya_umbrella"
2
+
3
+ LOG_FORMAT = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
4
+
5
+ LOG_MAX_BYTES = 1024 * 1024 * 5
maya_umbrella/core.py CHANGED
@@ -1,76 +1,117 @@
1
1
  # Import built-in modules
2
2
  import logging
3
- import re
4
3
 
5
4
  # Import third-party modules
6
5
  import maya.api.OpenMaya as om
6
+ import maya.cmds as cmds
7
7
 
8
8
  # Import local modules
9
9
  from maya_umbrella.filesystem import get_hooks
10
10
  from maya_umbrella.filesystem import get_vaccines
11
11
  from maya_umbrella.filesystem import load_hook
12
+ from maya_umbrella.log import setup_logger
13
+ from maya_umbrella.vaccine import MayaVirusCleaner
12
14
 
13
15
 
14
- class Defender(object):
16
+ class MayaVirusDefender(object):
15
17
  callback_ids = []
16
18
  remove_callbacks = []
17
19
  _bad_files = []
18
20
  _vaccines = []
19
21
  callback_maps = {
20
- "afterOpen": om.MSceneMessage.kAfterOpen,
21
- "afterImport": om.MSceneMessage.kAfterImport,
22
- "afterImportReference": om.MSceneMessage.kAfterImportReference,
23
- "afterLoadReference": om.MSceneMessage.kAfterLoadReference,
24
- "beforeSave": om.MSceneMessage.kBeforeSave,
25
- "beforeImport": om.MSceneMessage.kBeforeImport,
26
- "beforeLoadReference": om.MSceneMessage.kBeforeLoadReference,
27
- "beforeImportReference": om.MSceneMessage.kBeforeImportReference,
28
- "beforeMayaExiting": om.MSceneMessage.kMayaExiting,
22
+ "after_open": om.MSceneMessage.kAfterOpen,
23
+ "maya_initialized": om.MSceneMessage.kMayaInitialized,
24
+ "after_import": om.MSceneMessage.kAfterImport,
25
+ "after_import_reference": om.MSceneMessage.kAfterImportReference,
26
+ "after_load_reference": om.MSceneMessage.kAfterLoadReference,
27
+ "before_save": om.MSceneMessage.kBeforeSave,
28
+ "before_import": om.MSceneMessage.kBeforeImport,
29
+ "before_load_reference": om.MSceneMessage.kBeforeLoadReference,
30
+ "before_import_reference": om.MSceneMessage.kBeforeImportReference,
31
+ "maya_exiting": om.MSceneMessage.kMayaExiting,
29
32
  }
30
33
 
31
- def __init__(self):
32
- self.logger = logging.getLogger(__name__)
34
+ def __init__(self, auto_fix=True):
35
+ """Initialize the MayaVirusDefender.
36
+
37
+ Args:
38
+ auto_fix (bool): Whether to automatically fix issues.
39
+ """
40
+ logger = logging.getLogger(__name__)
41
+ self.auto_fix = auto_fix
42
+ self.logger = setup_logger(logger)
43
+ self.virus_cleaner = MayaVirusCleaner(self.logger)
33
44
  self.load_vaccines()
34
45
 
35
46
  def load_vaccines(self):
47
+ """Load all vaccines."""
36
48
  for vaccine in get_vaccines():
37
- self._vaccines.append(load_hook(vaccine).Vaccine(self.logger))
49
+ vaccine_class = load_hook(vaccine).Vaccine
50
+ try:
51
+ self._vaccines.append(vaccine_class(api=self.virus_cleaner, logger=self.logger))
52
+ except Exception as e:
53
+ self.logger.error("Error loading vaccine: %s", e)
38
54
 
39
55
  @property
40
56
  def vaccines(self):
57
+ """Get all loaded vaccines.
58
+
59
+ Returns:
60
+ list: A list of loaded vaccines.
61
+ """
41
62
  return self._vaccines
42
63
 
43
64
  def run_hooks(self):
44
- for hook in get_hooks():
45
- self.logger.info("run_hooks: %s", hook)
46
- load_hook(hook).hook(self.logger)
65
+ """Run all hooks, only works in non-batch mode."""
66
+ if not cmds.about(batch=True):
67
+ for hook_file in get_hooks():
68
+ self.logger.debug("run_hook: %s", hook_file)
69
+ try:
70
+ load_hook(hook_file).hook(virus_cleaner=self.virus_cleaner)
71
+ except Exception as e:
72
+ self.logger.error("Error running hook: %s", e)
47
73
 
48
- def setup(self):
49
- for name, callback in self.callback_maps.items():
50
- matched = re.search(r"(?P<name>(after|before))", name)
51
- if matched:
52
- method_name = matched.group("name")
53
- self.logger.info("setup %s.", name)
54
- self.callback_ids.append(
55
- {name: om.MSceneMessage.addCallback(callback, getattr(self, "{0}_callback".format(method_name)))}
56
- )
57
-
58
- self.start()
59
-
60
- def before_callback(self, *args, **kwargs):
61
- self.logger.info("before_callback.")
74
+ def collect(self):
75
+ """Collect all issues related to the Maya virus."""
62
76
  for vaccine in self.vaccines:
63
- vaccine.before_callback()
64
- self.run_hooks()
77
+ vaccine.collect_issues()
65
78
 
66
- def after_callback(self, *args, **kwargs):
67
- self.logger.info("after_callback.")
68
- for vaccine in self.vaccines:
69
- vaccine.after_callback()
70
- self.run_hooks()
79
+ def fix(self):
80
+ """Fix all issues related to the Maya virus."""
81
+ self.virus_cleaner.fix_all_issues()
82
+
83
+ def report(self):
84
+ """Report all issues related to the Maya virus."""
85
+ self.virus_cleaner.reset_all_issues()
86
+ self.collect()
87
+ self.virus_cleaner.report_all_issues()
88
+
89
+ def setup(self):
90
+ """Set up the MayaVirusDefender."""
91
+ self.virus_cleaner.setup_default_callbacks()
92
+ for name, callbacks in self.virus_cleaner.registered_callbacks.items():
93
+ maya_callback = self.callback_maps[name]
94
+ self.logger.debug("%s setup.", name)
95
+ for func in callbacks:
96
+ self.callback_ids.append(om.MSceneMessage.addCallback(maya_callback, func))
97
+ for name, callbacks in self.callback_maps.items():
98
+ self.logger.debug("setup callback %s.", name)
99
+ self.callback_ids.append(om.MSceneMessage.addCallback(callbacks, self._callback))
100
+
101
+ def _callback(self, *args, **kwargs):
102
+ """Callback function for MayaVirusDefender.
103
+
104
+ Args:
105
+ *args: Variable length argument list.
106
+ **kwargs: Arbitrary keyword arguments.
107
+ """
108
+ if self.auto_fix:
109
+ self.collect()
110
+ self.fix()
111
+ self.run_hooks()
112
+ else:
113
+ self.report()
71
114
 
72
115
  def start(self):
73
- for vaccine in self.vaccines:
74
- self.logger.info("process for vaccine: {0}".format(vaccine.virus_name))
75
- vaccine.process()
76
- self.run_hooks()
116
+ """Start the MayaVirusDefender."""
117
+ self._callback()
@@ -6,6 +6,10 @@ import os
6
6
  import random
7
7
  import shutil
8
8
  import string
9
+ import tempfile
10
+
11
+ # Import local modules
12
+ from maya_umbrella.constants import PACKAGE_NAME
9
13
 
10
14
 
11
15
  def this_root():
@@ -17,7 +21,7 @@ def safe_remove_file(file_path):
17
21
  """Remove the file at the given path without raising an error if the file does not exist."""
18
22
  try:
19
23
  os.remove(file_path)
20
- except (IOError, OSError):
24
+ except OSError:
21
25
  pass
22
26
 
23
27
 
@@ -25,7 +29,7 @@ def safe_rmtree(path):
25
29
  """Remove the directory at the given path without raising an error if the directory does not exist."""
26
30
  try:
27
31
  shutil.rmtree(path)
28
- except (IOError, OSError):
32
+ except OSError:
29
33
  pass
30
34
 
31
35
 
@@ -106,5 +110,34 @@ def get_hooks():
106
110
 
107
111
 
108
112
  def get_vaccines():
113
+ """Get a list of all vaccine files.
114
+
115
+ Returns:
116
+ list: A list of vaccine files.
117
+ """
109
118
  pattern = os.path.join(this_root(), "vaccines", "*.py")
110
119
  return [vaccine for vaccine in glob.glob(pattern) if "__init__" not in vaccine]
120
+
121
+
122
+ def get_log_root():
123
+ """Get the log root directory.
124
+
125
+ Returns:
126
+ str: The log root directory.
127
+ """
128
+ return os.getenv("MAYA_UMBRELLA_LOG_ROOT", tempfile.gettempdir())
129
+
130
+
131
+ def get_log_file():
132
+ """Get the path of the log file.
133
+
134
+ Returns:
135
+ str: The path of the log file.
136
+ """
137
+ root = get_log_root()
138
+ try:
139
+ os.makedirs(root)
140
+ except OSError:
141
+ pass
142
+ name = os.getenv("MAYA_UMBRELLA_LOG_NAME", PACKAGE_NAME)
143
+ return os.path.join(root, "{name}.log".format(name=name))
@@ -3,11 +3,16 @@ import maya.cmds as cmds
3
3
  import maya.mel as mel
4
4
 
5
5
 
6
- def hook(logger):
6
+ def hook(virus_cleaner):
7
7
  for plugin in ["Turtle.mll", "mayatomr.mll"]:
8
8
  if cmds.pluginInfo(plugin, q=1, loaded=1):
9
9
  cmds.unloadPlugin(plugin, f=1)
10
- turtle_nodes = ["TurtleRenderOptions", "TurtleUIOptions", "TurtleBakeLayerManager", "TurtleDefaultBakeLayer"]
10
+ turtle_nodes = [
11
+ "TurtleRenderOptions",
12
+ "TurtleUIOptions",
13
+ "TurtleBakeLayerManager",
14
+ "TurtleDefaultBakeLayer",
15
+ ]
11
16
  for node in turtle_nodes:
12
17
  if cmds.objExists(node):
13
18
  cmds.lockNode(node, lock=1)
@@ -2,28 +2,29 @@
2
2
  import maya.cmds as cmds
3
3
 
4
4
 
5
- def hook(logger):
5
+ def hook(virus_cleaner):
6
6
  unknown_node = cmds.ls(type="unknown")
7
7
  unknown_plugin = cmds.unknownPlugin(query=True, l=True)
8
8
  if unknown_node:
9
9
  for nodeObj in unknown_node:
10
10
  if cmds.objExists(nodeObj):
11
11
  if cmds.referenceQuery(nodeObj, isNodeReferenced=True):
12
- logger.warning("Node from reference, skip. {}".format(nodeObj))
12
+ virus_cleaner.logger.warning("Node from reference, skip. {}".format(nodeObj))
13
13
  continue
14
14
  if cmds.lockNode(nodeObj, query=True)[0]:
15
15
  try:
16
16
  cmds.lockNode(nodeObj, lock=False)
17
- except Exception as e:
18
- logger.warning("The node is locked and cannot be unlocked. skip {}".format(nodeObj))
17
+ except Exception:
18
+ virus_cleaner.logger.warning(
19
+ "The node is locked and cannot be unlocked. skip {}".format(nodeObj))
19
20
  continue
20
21
  try:
21
22
  cmds.delete(nodeObj)
22
- logger.warning("Delete node : {}".format(nodeObj))
23
- except Exception as e:
23
+ virus_cleaner.logger.warning("Delete node: {}".format(nodeObj))
24
+ except Exception:
24
25
  pass
25
26
 
26
27
  if unknown_plugin:
27
28
  for plugObj in unknown_plugin:
28
29
  cmds.unknownPlugin(plugObj, remove=True)
29
- logger.warning("Delete plug-in : {}".format(plugObj))
30
+ virus_cleaner.logger.warning("Delete plug-in: {}".format(plugObj))
@@ -2,13 +2,12 @@
2
2
  import maya.cmds as cmds
3
3
 
4
4
 
5
- def hook(logger):
5
+ def hook(virus_cleaner):
6
6
  for model_panel in cmds.getPanel(typ="modelPanel"):
7
-
8
7
  # Get callback of the model editor
9
8
  callback = cmds.modelEditor(model_panel, query=True, editorChanged=True)
10
9
 
11
10
  # If the callback is the erroneous `CgAbBlastPanelOptChangeCallback`
12
11
  if callback == "CgAbBlastPanelOptChangeCallback":
13
- # Remove the callbacks from the editor
12
+ virus_cleaner.logger.info("Remove the callbacks from the editor")
14
13
  cmds.modelEditor(model_panel, edit=True, editorChanged="")
@@ -3,12 +3,17 @@ import maya.cmds as cmds
3
3
  import maya.mel as mel
4
4
 
5
5
 
6
- def hook(logger):
7
- mel.eval('global proc onModelChange3dc(string $a){}')
8
-
6
+ def hook(virus_cleaner):
7
+ try:
8
+ mel.eval("global proc onModelChange3dc(string $a){}")
9
+ except Exception:
10
+ pass
9
11
  try:
10
12
  cmds.delete("fixCgAbBlastPanelOptChangeCallback")
11
- except:
13
+ except Exception:
14
+ pass
15
+ try:
16
+ script = "global proc CgAbBlastPanelOptChangeCallback(string $i){}"
17
+ mel.eval(script)
18
+ except Exception:
12
19
  pass
13
- script = "global proc CgAbBlastPanelOptChangeCallback(string $i){}"
14
- mel.eval(script)
maya_umbrella/log.py ADDED
@@ -0,0 +1,37 @@
1
+ # Import built-in modules
2
+ import logging.handlers
3
+ import os
4
+
5
+ # Import local modules
6
+ from maya_umbrella.constants import LOG_FORMAT
7
+ from maya_umbrella.constants import LOG_MAX_BYTES
8
+ from maya_umbrella.constants import PACKAGE_NAME
9
+ from maya_umbrella.filesystem import get_log_file
10
+
11
+
12
+ def setup_logger(logger=None, logfile=None, log_level=None):
13
+ """Set up the logger with the specified log file and log level.
14
+
15
+ Args:
16
+ logger (logging.Logger, optional): The logger to set up. Defaults to the logger for the package.
17
+ logfile (str, optional): The path to the log file. Defaults to the log file returned by `get_log_file()`.
18
+ log_level (int, optional): The log level. Defaults to `logging.INFO`.
19
+
20
+ Returns:
21
+ logging.Logger: The set up logger.
22
+ """
23
+ logger = logger or logging.getLogger(PACKAGE_NAME)
24
+ log_level = log_level or os.getenv("MAYA_UMBRELLA_LOG_LEVEL", "INFO")
25
+ logger.setLevel(log_level)
26
+ logfile = logfile or get_log_file()
27
+ if not len(logger.handlers):
28
+ filehandler = logging.handlers.RotatingFileHandler(
29
+ logfile,
30
+ mode="a",
31
+ backupCount=7,
32
+ delay=True,
33
+ maxBytes=LOG_MAX_BYTES,
34
+ )
35
+ filehandler.setFormatter(logging.Formatter(LOG_FORMAT))
36
+ logger.addHandler(filehandler)
37
+ return logger
maya_umbrella/vaccine.py CHANGED
@@ -1,4 +1,5 @@
1
1
  # Import built-in modules
2
+ from collections import defaultdict
2
3
  import glob
3
4
  import logging
4
5
  import os
@@ -11,70 +12,211 @@ from maya_umbrella.filesystem import safe_remove_file
11
12
  from maya_umbrella.filesystem import safe_rmtree
12
13
 
13
14
 
14
- class BaseVaccine(object):
15
- virus_name = None
16
-
15
+ class MayaVirusCleaner(object):
17
16
  _bad_files = []
17
+ _bad_nodes = []
18
+ _bad_script_nodes = []
19
+ _bad_script_jobs = []
20
+ _registered_callbacks = defaultdict(list)
21
+ _fix_funcs = []
18
22
 
19
- def __init__(self, logger=None):
20
- self._logger = logger or logging.getLogger(__name__)
23
+ def __init__(self, logger=None, auto_fix=True):
24
+ self.logger = logger or logging.getLogger(__name__)
25
+ self.auto_fix = auto_fix
21
26
 
22
27
  @property
23
28
  def user_app_dir(self):
29
+ """Return the user application directory."""
24
30
  return cmds.internalVar(userAppDir=True)
25
31
 
32
+ @property
33
+ def maya_file(self):
34
+ """Return the current Maya file."""
35
+ return cmds.file(q=True, sn=True)
36
+
26
37
  @property
27
38
  def maya_install_root(self):
39
+ """Return the Maya installation root directory."""
28
40
  return os.environ["MAYA_LOCATION"]
29
41
 
30
42
  @property
31
43
  def user_script_path(self):
44
+ """Return the user script directory."""
32
45
  return cmds.internalVar(userScriptDir=True)
33
46
 
34
47
  @property
35
48
  def local_script_path(self):
49
+ """Return the local script directory."""
36
50
  return os.path.join(self.user_app_dir, "scripts")
37
51
 
38
52
  @property
39
53
  def bad_files(self):
40
- return []
54
+ """Return a list of bad files."""
55
+ return [path for path in list(set(self._bad_files)) if os.path.exists(path)]
41
56
 
42
57
  @property
43
58
  def bad_nodes(self):
44
- return []
59
+ """Return a list of bad nodes."""
60
+ return list(set(self._bad_nodes))
45
61
 
46
62
  @property
47
63
  def bad_script_nodes(self):
48
- return []
64
+ """Return a list of bad script nodes."""
65
+ return list(set(self._bad_script_nodes))
66
+
67
+ @property
68
+ def bad_script_jobs(self):
69
+ """Return a list of bad script jobs."""
70
+ return list(set(self._bad_script_jobs))
71
+
72
+ def callback_remove_rename_temp_files(self, *args, **kwargs):
73
+ """
74
+ Remove temporary files in the local script path.
75
+ """
76
+ self.logger.info("Removing temporary files in %s", self.local_script_path)
77
+ [safe_remove_file(temp_file) for temp_file in glob.glob(os.path.join(self.local_script_path, "._*"))]
78
+
79
+ @property
80
+ def registered_callbacks(self):
81
+ return self._registered_callbacks
82
+
83
+ def add_bad_files(self, files):
84
+ self._bad_files.extend(files)
85
+
86
+ def add_bad_file(self, file):
87
+ self._bad_files.append(file)
88
+
89
+ def add_bad_nodes(self, nodes):
90
+ self._bad_nodes.extend(nodes)
91
+
92
+ def add_bad_node(self, node):
93
+ self._bad_nodes.append(node)
94
+
95
+ def add_bad_script_jobs(self, jobs):
96
+ self._bad_script_jobs.extend(jobs)
97
+
98
+ def add_bad_script_job(self, job):
99
+ self._bad_script_jobs.append(job)
100
+
101
+ def add_bad_script_nodes(self, nodes):
102
+ self._bad_script_nodes.extend(nodes)
103
+
104
+ def add_bad_script_node(self, node):
105
+ self._bad_script_nodes.append(node)
49
106
 
50
- def collect(self):
51
- self._bad_files.extend([temp_file for temp_file in glob.glob(os.path.join(self.local_script_path, "._*"))])
107
+ def register_callback(self, callback_name, callback):
108
+ """Register a callback to be executed before or after processing."""
109
+ self._registered_callbacks[callback_name].append(callback)
52
110
 
53
- def remove_bad_files(self):
54
- self.collect()
111
+ def add_after_open_callback(self, callback):
112
+ self.register_callback("after_open", callback)
113
+
114
+ def add_maya_initialized_callback(self, callback):
115
+ self.register_callback("maya_initialized", callback)
116
+
117
+ def add_after_import_callback(self, callback):
118
+ self.register_callback("after_import", callback)
119
+
120
+ def add_after_import_reference_callback(self, callback):
121
+ self.register_callback("after_import_reference", callback)
122
+
123
+ def add_after_load_reference_callback(self, callback):
124
+ self.register_callback("after_load_reference", callback)
125
+
126
+ def add_before_save_callback(self, callback):
127
+ self.register_callback("before_save", callback)
128
+
129
+ def add_before_import_callback(self, callback):
130
+ self.register_callback("before_import", callback)
131
+
132
+ def add_before_load_reference_callback(self, callback):
133
+ self.register_callback("before_load_reference", callback)
134
+
135
+ def add_before_import_reference_callback(self, callback):
136
+ self.register_callback("before_import_reference", callback)
137
+
138
+ def add_maya_exiting_callback(self, callback):
139
+ self.register_callback("maya_exiting", callback)
140
+
141
+ def setup_default_callbacks(self):
142
+ self.add_maya_initialized_callback(self.callback_remove_rename_temp_files)
143
+ self.add_maya_exiting_callback(self.callback_remove_rename_temp_files)
144
+
145
+ def add_fix_function(self, func):
146
+ self._fix_funcs.append(func)
147
+
148
+ # fix
149
+
150
+ def fix_script_jobs(self):
151
+ for script_job in self.bad_script_jobs:
152
+ script_num = int(script_job.split(":", 1)[0])
153
+ self.logger.info("Kill script job %s", script_job)
154
+ cmds.scriptJob(kill=script_num, force=True)
155
+ self._bad_script_jobs.remove(script_job)
156
+
157
+ def fix_bad_files(self):
55
158
  for file_ in self.bad_files:
56
159
  if os.path.exists(file_):
57
160
  if os.path.isfile(file_):
58
- self._logger.info("Removing {}".format(file_))
161
+ self.logger.info("Removing %s", file_)
59
162
  safe_remove_file(file_)
163
+ self._bad_files.remove(file_)
60
164
  else:
61
- self._logger.info("Removing folder {}".format(file_))
165
+ self.logger.info("Removing folder %s", file_)
62
166
  safe_rmtree(file_)
167
+ self._bad_files.remove(file_)
63
168
 
64
- def delete_bad_nodes(self):
169
+ def fix_bad_nodes(self):
65
170
  for node in self.bad_nodes:
66
- self._logger.info("Deleting %s", node)
67
- cmds.lockNode(node, l=False)
68
- cmds.delete(node)
171
+ self.logger.info("Deleting %s", node)
172
+ try:
173
+ cmds.lockNode(node, lock=False)
174
+ except ValueError:
175
+ pass
176
+ try:
177
+ cmds.delete(node)
178
+ except ValueError:
179
+ pass
180
+ self._bad_nodes.remove(node)
181
+
182
+ def fix_all_issues(self):
183
+ """Fix all issues related to the Maya virus."""
184
+ self.fix_bad_files()
185
+ self.fix_bad_nodes()
186
+ self.fix_script_jobs()
187
+ for func in self._fix_funcs:
188
+ func()
189
+
190
+ def report_all_issues(self):
191
+ """Report all issues related to the Maya virus."""
192
+ self.logger.info("Bad files: %s", self.bad_files)
193
+ self.logger.info("Bad nodes: %s", self.bad_nodes)
194
+ self.logger.info("Bad script jobs: %s", self.bad_script_jobs)
195
+
196
+ def reset_all_issues(self):
197
+ """Reset all issues related to the Maya virus."""
198
+ self._bad_files = []
199
+ self._bad_nodes = []
200
+ self._bad_script_nodes = []
201
+ self._bad_script_jobs = []
202
+
203
+
204
+ class AbstractVaccine(object):
205
+ virus_name = None
206
+
207
+ def __init__(self, api, logger):
208
+ """Abstract class for Vaccine.
209
+
210
+ Args:
211
+ api (MayaVirusCleaner): The VaccineAPI instance.
212
+ logger (Logger): The logger instance.
69
213
 
70
- def before_callback(self, *args, **kwargs):
71
- self.remove_bad_files()
72
- self.delete_bad_nodes()
214
+ """
215
+ self.api = api
216
+ self.logger = logger
73
217
 
74
- def after_callback(self, *args, **kwargs):
75
- self.remove_bad_files()
76
- self.delete_bad_nodes()
218
+ def collect_issues(self):
219
+ raise NotImplementedError
77
220
 
78
- def process(self):
79
- self.before_callback()
80
- self.after_callback()
221
+ def report_issue(self, name):
222
+ self.logger.warning("%s: Infected by Malware!", name)
@@ -1,19 +1,17 @@
1
1
  # Import built-in modules
2
- import os.path
2
+ import os
3
3
 
4
4
  # Import local modules
5
- from maya_umbrella.vaccine import BaseVaccine
5
+ from maya_umbrella.vaccine import AbstractVaccine
6
6
 
7
7
 
8
- class Vaccine(BaseVaccine):
8
+ class Vaccine(AbstractVaccine):
9
9
  virus_name = "pu tian tong qi"
10
10
 
11
- def __init__(self, logger=None):
12
- super(Vaccine, self).__init__(logger)
13
-
14
- @property
15
- def bad_files(self):
16
- return [
17
- os.path.join(self.local_script_path, "fuckVirus.py"),
18
- os.path.join(self.local_script_path, "fuckVirus.pyc"),
19
- ]
11
+ def collect_issues(self):
12
+ self.api.add_bad_files(
13
+ [
14
+ os.path.join(self.api.local_script_path, "fuckVirus.py"),
15
+ os.path.join(self.api.local_script_path, "fuckVirus.pyc"),
16
+ ],
17
+ )
@@ -7,22 +7,14 @@ import maya.cmds as cmds
7
7
  # Import local modules
8
8
  from maya_umbrella.filesystem import read_file
9
9
  from maya_umbrella.filesystem import rename
10
- from maya_umbrella.vaccine import BaseVaccine
10
+ from maya_umbrella.vaccine import AbstractVaccine
11
11
 
12
12
 
13
- class Vaccine(BaseVaccine):
13
+ class Vaccine(AbstractVaccine):
14
14
  virus_name = "zei jian kang"
15
- _bad_files = []
16
15
 
17
- def __init__(self, logger=None):
18
- super(Vaccine, self).__init__(logger)
19
-
20
- @property
21
- def bad_files(self):
22
- return [os.path.join(self.local_script_path, "vaccine.py"), os.path.join(self.local_script_path, "vaccine.pyc")]
23
-
24
- def _get_nodes(self):
25
- bad_script_nodes = []
16
+ def collect_bad_nodes(self):
17
+ """Collect all bad nodes related to the virus."""
26
18
  for script_node in cmds.ls(type="script"):
27
19
  if cmds.referenceQuery(script_node, isNodeReferenced=True):
28
20
  continue
@@ -32,34 +24,37 @@ class Vaccine(BaseVaccine):
32
24
  if not script_string:
33
25
  continue
34
26
  if "internalVar" in script_string or "userSetup" in script_string or "fuckVirus" in script_string:
35
- self._logger.warning("script node {} has internalVar or userSetup or fuckVirus".format(script_node))
36
- bad_script_nodes.append(script_node)
37
- return bad_script_nodes
38
-
39
- @property
40
- def bad_nodes(self):
41
- return self._get_nodes()
42
-
43
- def check_usersetup_py(self):
27
+ self.report_issue(script_node)
28
+ self.api.add_bad_node(script_node)
29
+
30
+ def collect_issues(self):
31
+ """Collect all issues related to the virus."""
32
+ self.api.add_bad_files(
33
+ [
34
+ os.path.join(self.api.local_script_path, "vaccine.py"),
35
+ os.path.join(self.api.local_script_path, "vaccine.pyc"),
36
+ ],
37
+ )
38
+ self.collect_bad_usersetup_py()
39
+ self.collect_bad_nodes()
40
+
41
+ def collect_bad_usersetup_py(self):
42
+ """Collect all bad userSetup.py files related to the virus."""
44
43
  for usersetup_py in [
45
- os.path.join(self.local_script_path, "vaccine.py"),
46
- os.path.join(self.user_script_path, "vaccine.py"),
47
- os.path.join(self.local_script_path, "userSetup.py"),
48
- os.path.join(self.user_script_path, "userSetup.py"),
44
+ os.path.join(self.api.local_script_path, "vaccine.py"),
45
+ os.path.join(self.api.user_script_path, "vaccine.py"),
46
+ os.path.join(self.api.local_script_path, "userSetup.py"),
47
+ os.path.join(self.api.user_script_path, "userSetup.py"),
49
48
  ]:
50
49
  if os.path.exists(usersetup_py):
51
50
  data = read_file(usersetup_py)
52
51
  if "petri_dish_path = cmds.internalVar(userAppDir=True) + 'scripts/userSetup.py" in data:
53
- self._logger.warning("vaccine.py found : Infected by Malware!")
54
- self._bad_files.append(rename(usersetup_py))
52
+ self.report_issue("vaccine.py")
53
+ self.api.add_bad_file(rename(usersetup_py))
55
54
 
56
55
  if (
57
56
  "cmds.evalDeferred('leukocyte = vaccine.phage()')" in data
58
57
  and "cmds.evalDeferred('leukocyte.occupation()')" in data
59
58
  ):
60
- self._logger.warning("userSetup.py : Infected by Malware!")
61
- self._bad_files.append(rename(usersetup_py))
62
-
63
- def before_callback(self, *args, **kwargs):
64
- self.check_usersetup_py()
65
- super(Vaccine, self).before_callback(args, kwargs)
59
+ self.report_issue("userSetup.py")
60
+ self.api.add_bad_file(rename(usersetup_py))
@@ -9,65 +9,54 @@ import maya.cmds as cmds
9
9
  # Import local modules
10
10
  from maya_umbrella.filesystem import read_file
11
11
  from maya_umbrella.filesystem import rename
12
- from maya_umbrella.vaccine import BaseVaccine
12
+ from maya_umbrella.vaccine import AbstractVaccine
13
13
 
14
14
 
15
- class Vaccine(BaseVaccine):
15
+ class Vaccine(AbstractVaccine):
16
16
  virus_name = "virus2024429"
17
17
  hik_regex = r"python\(\"import base64;\s*pyCode\s*=\s*base64\.urlsafe_b64decode\([\'\"].*?[\"\']\);\s*exec\s*\(\s*pyCode\s*\)\"\)\s*;"
18
- _bad_files = []
19
18
 
20
- def __init__(self, logger=None):
21
- super(Vaccine, self).__init__(logger)
22
-
23
- @property
24
- def bad_files(self):
25
- return self._bad_files
26
-
27
- @property
28
- def get_syssst_dir(self):
29
- return os.path.join(os.getenv("APPDATA"), "syssst")
30
-
31
- def _get_nodes(self):
32
- bad_expression = []
19
+ def collect_bad_nodes(self):
20
+ """Collect all bad nodes related to the virus."""
33
21
  for script_node in cmds.ls(type="script"):
34
22
  if cmds.referenceQuery(script_node, isNodeReferenced=True):
35
23
  continue
36
24
  # check uifiguration
37
25
  if cmds.objExists("{}.KGMScriptProtector".format(script_node)):
38
- bad_expression.append(script_node)
26
+ self.api.add_bad_node(script_node)
39
27
  # check vaccine
40
28
  if "_gene" in script_node:
41
- bad_expression.append(script_node)
42
-
43
- return bad_expression
29
+ self.api.add_bad_node(script_node)
44
30
 
45
- def check_usersetup_mel(self):
31
+ def collect_bad_mel_files(self):
32
+ """Collect all bad MEL files related to the virus."""
46
33
  # check usersetup.mel
47
34
  # C:/Users/hallong/Documents/maya/scripts/usersetup.mel
48
35
  # C:/Users/hallong/Documents/maya/xxxx/scripts/usersetup.mel
49
36
  for usersetup_mel in [
50
- os.path.join(self.local_script_path, "usersetup.mel"),
51
- os.path.join(self.user_script_path, "usersetup.mel"),
37
+ os.path.join(self.api.local_script_path, "usersetup.mel"),
38
+ os.path.join(self.api.user_script_path, "usersetup.mel"),
52
39
  ]:
53
40
  if os.path.exists(usersetup_mel):
54
41
  data = read_file(usersetup_mel)
55
42
  if "import base64; pyCode = base64.urlsafe_b64decode" in data:
56
- self._bad_files.append(rename(usersetup_mel))
43
+ self.api.add_bad_file(rename(usersetup_mel))
57
44
 
58
- @property
59
- def bad_nodes(self):
60
- return self._get_nodes()
45
+ def collect_script_jobs(self):
46
+ """Collect all script jobs related to the virus."""
47
+ virus_gene = [
48
+ "leukocyte",
49
+ "execute",
50
+ ]
61
51
 
62
- def before_callback(self, *args, **kwargs):
63
- self._bad_files.extend([self.get_syssst_dir])
64
- self.check_usersetup_mel()
65
- self.fix_hik_files()
66
- self.fix_script_job()
67
- super(Vaccine, self).before_callback(args, kwargs)
52
+ for script_job in cmds.scriptJob(listJobs=True):
53
+ for virus in virus_gene:
54
+ if virus in script_job:
55
+ self.api.add_bad_script_jobs(script_job)
68
56
 
69
- def fix_hik_files(self):
70
- pattern = os.path.join(self.maya_install_root, "resources/l10n/*/plug-ins/mayaHIK.pres.mel")
57
+ def fix_bad_hik_files(self):
58
+ """Fix all bad HIK files related to the virus."""
59
+ pattern = os.path.join(self.api.maya_install_root, "resources/l10n/*/plug-ins/mayaHIK.pres.mel")
71
60
  for hik_mel in glob.glob(pattern):
72
61
  with open(hik_mel, "rb") as f:
73
62
  data = f.read()
@@ -76,35 +65,14 @@ class Vaccine(BaseVaccine):
76
65
  except TypeError:
77
66
  check = []
78
67
  if len(check) > 0:
68
+ self.report_issue(hik_mel)
79
69
  with open(hik_mel, "wb") as f:
80
70
  f.write(re.sub(self.hik_regex, "", data))
81
- self._logger.warning("Remove virus code from {}".format(hik_mel))
82
-
83
- def fix_script_job(self):
84
- virus_gene = [
85
- "leukocyte",
86
- "execute",
87
- ]
88
-
89
- def get_virus_script_jobs():
90
- """Traverse the list of virus script job name.
91
- Returns:
92
- list: Malicious virus script job name.
93
- """
94
- return [
95
- scriptjob
96
- for scriptjob in cmds.scriptJob(listJobs=True)
97
- for virus in virus_gene
98
- if virus in scriptjob
99
- ]
100
-
101
- for script_job in get_virus_script_jobs():
102
- script_num = int(script_job.split(":", 1)[0])
103
- self._logger.info("Kill script job {}".format(script_job))
104
- cmds.scriptJob(kill=script_num, force=True)
105
-
106
- def after_callback(self, *args, **kwargs):
107
- """After callback."""
108
- self.check_usersetup_mel()
109
- self.fix_hik_files()
110
- super(Vaccine, self).after_callback(args, kwargs)
71
+ self.logger.info("Remove virus code from %s", hik_mel)
72
+
73
+ def collect_issues(self):
74
+ """Collect all issues related to the virus."""
75
+ self.api.add_bad_file(os.path.join(os.getenv("APPDATA"), "syssst"))
76
+ self.collect_bad_mel_files()
77
+ self.collect_bad_nodes()
78
+ self.api.add_fix_function(self.fix_bad_hik_files)
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.1
2
+ Name: maya_umbrella
3
+ Version: 0.4.1
4
+ Summary: Check and fix maya virus.
5
+ Home-page: https://github.com/loonghao/maya_umbrella
6
+ License: MIT
7
+ Keywords: notifiers,python,Maya,dcc
8
+ Author: longhao
9
+ Author-email: hal.long@outlook.com
10
+ Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 2
13
+ Classifier: Programming Language :: Python :: 2.7
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.6
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Project-URL: Documentation, https://github.com/loonghao/maya_umbrella
23
+ Project-URL: Repository, https://github.com/loonghao/maya_umbrella
24
+ Description-Content-Type: text/markdown
25
+
26
+ # maya-umbrella
27
+
28
+ [![Python Version](https://img.shields.io/pypi/pyversions/maya-umbrella)](https://img.shields.io/pypi/pyversions/maya-umbrella)
29
+ [![Nox](https://img.shields.io/badge/%F0%9F%A6%8A-Nox-D85E00.svg)](https://github.com/wntrblm/nox)
30
+ [![PyPI Version](https://img.shields.io/pypi/v/maya-umbrella?color=green)](https://pypi.org/project/maya-umbrella/)
31
+ [![Downloads Status](https://img.shields.io/pypi/dw/maya-umbrella)](https://pypi.org/project/maya-umbrella/)
32
+ [![Downloads](https://pepy.tech/badge/maya-umbrella)](https://pepy.tech/project/maya-umbrella)
33
+ [![License](https://img.shields.io/pypi/l/maya-umbrella)](https://pypi.org/project/maya-umbrella/)
34
+ [![PyPI Format](https://img.shields.io/pypi/format/maya-umbrella)](https://pypi.org/project/maya-umbrella/)
35
+ [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/loonghao/maya-umbrella/graphs/commit-activity)
36
+ [![maya-2024](https://img.shields.io/badge/maya-2024-green)](https://img.shields.io/badge/maya-2024-green)
37
+ [![maya-2023](https://img.shields.io/badge/maya-2023-green)](https://img.shields.io/badge/maya-2023-green)
38
+ [![maya-2022](https://img.shields.io/badge/maya-2022-green)](https://img.shields.io/badge/maya-2022-green)
39
+ [![maya-2021](https://img.shields.io/badge/maya-2021-green)](https://img.shields.io/badge/maya-2021-green)
40
+ [![maya-2020](https://img.shields.io/badge/maya-2020-green)](https://img.shields.io/badge/maya-2020-green)
41
+ [![maya-2019](https://img.shields.io/badge/maya-2019-green)](https://img.shields.io/badge/maya-2019-green)
42
+ [![maya-2018](https://img.shields.io/badge/maya-2018-green)](https://img.shields.io/badge/maya-2018-green)
43
+
44
+ <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
45
+ [![All Contributors](https://img.shields.io/badge/all_contributors-4-orange.svg?style=flat-square)](#contributors-)
46
+ <!-- ALL-CONTRIBUTORS-BADGE:END -->
47
+
48
+ Check and fix maya virus.
49
+
50
+
51
+ # 发开环境设置
52
+
53
+ 通过虚拟环境去设置开发环境, 推荐python-3.8以上的版本
54
+
55
+ ```shell
56
+ pip install nox poetry
57
+ ```
58
+
59
+ # 开发调试
60
+
61
+ ```shell
62
+ nox -s maya-2020
63
+ ```
64
+
65
+ ## 在maya中测试
66
+
67
+ 通过`nox -s maya-xxx`, 启动maya.
68
+ nox会动态根据你本地安装得maya去注册nox session, 比如你本地安装了`maya-2020`,那么通过`nox -s maya-2018`
69
+
70
+ 启动maya后在脚本编辑器中执行下面得代码,就会动态的从`<repo>/tests/virus/`里面去open ma文件去进行测试.
71
+
72
+ ```python
73
+ import manual_test_in_maya
74
+
75
+ manual_test_in_maya.start()
76
+ ```
77
+
78
+ ## 增加新的疫苗
79
+
80
+ 在`<repo>/maya_umbrella/vaccines/` 新建一个py, 因为有很多病毒没有具体的名字代号,我们统一以`vaccine<id>.py`
81
+ 继承`from maya_umbrella.vaccine import AbstractVaccine`并且class名字叫`Vaccine`即可, 然后去写具体的收集病毒逻辑
82
+
83
+ ## 代码检查
84
+
85
+ 我们可以利用封装好的`nox`命令去执行进行代码检查
86
+
87
+ ```shell
88
+ nox -s ruff_check
89
+ ```
90
+
91
+ # 环境变量
92
+
93
+ 我们可以通过下列环境变量去修改maya_umbrella的一些设置,方便有pipeline的公司可以更好的集成
94
+
95
+ 修改maya umbrella的日志保存目录,默认是windows temp目录
96
+
97
+ ```shell
98
+ MAYA_UMBRELLA_LOG_ROOT
99
+ ```
100
+
101
+ 修改maya umbrella的日志文件名称, 默认是`maya_umbrella`
102
+
103
+ ```shell
104
+ MAYA_UMBRELLA_LOG_NAME
105
+ ```
106
+
107
+ 设置日志级别,默认是info, 可以是debug可以看到更多的日志信息
108
+
109
+ ```shell
110
+ MAYA_UMBRELLA_LOG_LEVEL
111
+ ```
112
+
113
+ ## Contributors ✨
114
+
115
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
116
+
117
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
118
+ <!-- prettier-ignore-start -->
119
+ <!-- markdownlint-disable -->
120
+ <table>
121
+ <tbody>
122
+ <tr>
123
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/loonghao"><img src="https://avatars1.githubusercontent.com/u/13111745?v=4?s=100" width="100px;" alt="Hal"/><br /><sub><b>Hal</b></sub></a><br /><a href="https://github.com/loonghao/maya_umbrella/commits?author=loonghao" title="Code">💻</a> <a href="#infra-loonghao" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/loonghao/maya_umbrella/commits?author=loonghao" title="Tests">⚠️</a></td>
124
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/hotwinter0"><img src="https://avatars.githubusercontent.com/u/106237305?v=4?s=100" width="100px;" alt="hotwinter0"/><br /><sub><b>hotwinter0</b></sub></a><br /><a href="https://github.com/loonghao/maya_umbrella/commits?author=hotwinter0" title="Tests">⚠️</a> <a href="https://github.com/loonghao/maya_umbrella/commits?author=hotwinter0" title="Code">💻</a></td>
125
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/lingyunfx"><img src="https://avatars.githubusercontent.com/u/73666629?v=4?s=100" width="100px;" alt="lingyunfx"/><br /><sub><b>lingyunfx</b></sub></a><br /><a href="https://github.com/loonghao/maya_umbrella/commits?author=lingyunfx" title="Tests">⚠️</a></td>
126
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/yjjjj"><img src="https://avatars.githubusercontent.com/u/12741735?v=4?s=100" width="100px;" alt="yjjjj"/><br /><sub><b>yjjjj</b></sub></a><br /><a href="https://github.com/loonghao/maya_umbrella/commits?author=yjjjj" title="Tests">⚠️</a> <a href="https://github.com/loonghao/maya_umbrella/commits?author=yjjjj" title="Code">💻</a></td>
127
+ </tr>
128
+ </tbody>
129
+ </table>
130
+
131
+ <!-- markdownlint-restore -->
132
+ <!-- prettier-ignore-end -->
133
+
134
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
135
+
136
+ This project follows the [all-contributors](https://allcontributors.org) specification.
137
+ Contributions of any kind are welcome!
138
+
@@ -0,0 +1,20 @@
1
+ maya_umbrella/__init__.py,sha256=039hJesYFSc1aXypFRo1PVQvqowJJ9Q_bAdfcEQI2qk,106
2
+ maya_umbrella/__version__.py,sha256=pMtTmSUht-XtbR_7Doz6bsQqopJJd8rZ8I8zy2HwwoA,22
3
+ maya_umbrella/constants.py,sha256=SuD8OP8e0Kh3a9ohyS5_MXjo5pHNQ8MWEPtJ6puZfIU,130
4
+ maya_umbrella/core.py,sha256=kUaDyDPwD92e2D3GIRPRUMZ2MAnf7DlaGBbBl753NwY,4112
5
+ maya_umbrella/filesystem.py,sha256=EwOtPvl9UhjOu2YYW1hyIbmqsdcseu6ye0Bnuzf2khU,4125
6
+ maya_umbrella/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ maya_umbrella/hooks/delete_turtle.py,sha256=G5zzMFn9L9lSydvFiscUIqSgLbHZlWUV0lfUtnhvQUw,734
8
+ maya_umbrella/hooks/delete_unknown_plugin_node.py,sha256=PxufrH9eqUP6Y28bseTWmCaO_0M4zeHzexIJpws9Q2U,1246
9
+ maya_umbrella/hooks/fix_model_panel.py,sha256=DLXPo_E9Q1C6a3Dpye4jWCSqfjZxG0iBduMhORV5vKo,546
10
+ maya_umbrella/hooks/fix_on_model_change_3dc.py,sha256=PPnU70Ed_TW9j5nkQnbQ2EEGVo5jm5T6gimzOQYbgCY,457
11
+ maya_umbrella/log.py,sha256=IJweFEBTThL6_mW86jAAPKyWNvI79CrMifs5vO3t6ks,1338
12
+ maya_umbrella/vaccine.py,sha256=W6JhnA7j4rjltF8jIoAXVMFq3v1d_dTyZaQGhq2dkWk,7033
13
+ maya_umbrella/vaccines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ maya_umbrella/vaccines/vaccine1.py,sha256=oM41xlB49YPl_S760TqFyosDVIAIZOzsqkcL5sh679s,430
15
+ maya_umbrella/vaccines/vaccine2.py,sha256=XRnQFSvbTXxgr8FZ8eU6s_h_Uwyt429a93gyfDyglic,2526
16
+ maya_umbrella/vaccines/vaccine3.py,sha256=KLGSSLPqTjrVjcrT65wQNuM31MoDBFTc0kmAoLzu2NI,3053
17
+ maya_umbrella-0.4.1.dist-info/LICENSE,sha256=tJf0Pz8q_65AjEkm3872K1cl4jGil28vJO5Ko_LhUqc,1060
18
+ maya_umbrella-0.4.1.dist-info/METADATA,sha256=Vx3UbEyZHJnpJtVk2nak1RUgU4ZZ1MDjEm84CcdzcJo,6710
19
+ maya_umbrella-0.4.1.dist-info/WHEEL,sha256=IrRNNNJ-uuL1ggO5qMvT1GGhQVdQU54d6ZpYqEZfEWo,92
20
+ maya_umbrella-0.4.1.dist-info/RECORD,,
@@ -1,85 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: maya_umbrella
3
- Version: 0.3.0
4
- Summary: Check and fix maya virus.
5
- Home-page: https://github.com/loonghao/maya_umbrella
6
- License: MIT
7
- Keywords: notifiers,python,Maya,dcc
8
- Author: longhao
9
- Author-email: hal.long@outlook.com
10
- Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Programming Language :: Python :: 2
13
- Classifier: Programming Language :: Python :: 2.7
14
- Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.6
16
- Classifier: Programming Language :: Python :: 3.7
17
- Classifier: Programming Language :: Python :: 3.8
18
- Classifier: Programming Language :: Python :: 3.9
19
- Classifier: Programming Language :: Python :: 3.10
20
- Classifier: Programming Language :: Python :: 3.11
21
- Classifier: Programming Language :: Python :: 3.12
22
- Project-URL: Documentation, https://github.com/loonghao/maya_umbrella
23
- Project-URL: Repository, https://github.com/loonghao/maya_umbrella
24
- Description-Content-Type: text/markdown
25
-
26
- # maya_umbrella
27
-
28
- Check and fix maya virus.
29
-
30
- [![Nox](https://img.shields.io/badge/%F0%9F%A6%8A-Nox-D85E00.svg)](https://github.com/wntrblm/nox)
31
- <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
32
- [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-)
33
- <!-- ALL-CONTRIBUTORS-BADGE:END -->
34
-
35
-
36
-
37
-
38
-
39
- # 发开环境设置
40
- python 版本 3.9
41
-
42
- ```shell
43
- pip install nox poetry
44
- ```
45
-
46
- # 开发调试
47
-
48
- ```shell
49
- nox -s maya-2020
50
- ```
51
-
52
- ## 在maya中测试
53
- 通过`nox -s maya-xxx`, 启动maya.
54
- nox会动态根据你本地安装得maya去注册nox session, 比如你本地安装了`maya-2020`,那么通过`nox -s maya-2018`
55
-
56
- 启动maya后在脚本编辑器中执行下面得代码,就会动态的从`<repo>/tests/virus/`里面去open ma文件去进行测试.
57
- ```python
58
- import manual_test_in_maya
59
-
60
- manual_test_in_maya.start()
61
- ```
62
-
63
- ## Contributors ✨
64
-
65
- Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
66
-
67
- <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
68
- <!-- prettier-ignore-start -->
69
- <!-- markdownlint-disable -->
70
- <table>
71
- <tbody>
72
- <tr>
73
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/loonghao"><img src="https://avatars1.githubusercontent.com/u/13111745?v=4?s=100" width="100px;" alt="Hal"/><br /><sub><b>Hal</b></sub></a><br /><a href="https://github.com/loonghao/maya_umbrella/commits?author=loonghao" title="Code">💻</a></td>
74
- </tr>
75
- </tbody>
76
- </table>
77
-
78
- <!-- markdownlint-restore -->
79
- <!-- prettier-ignore-end -->
80
-
81
- <!-- ALL-CONTRIBUTORS-LIST:END -->
82
-
83
- This project follows the [all-contributors](https://allcontributors.org) specification.
84
- Contributions of any kind are welcome!
85
-
@@ -1,18 +0,0 @@
1
- maya_umbrella/__init__.py,sha256=XrGd2g7JVWk-K48K1SHETv1su1cDUj4rzeu36U8VOSc,88
2
- maya_umbrella/__version__.py,sha256=VrXpHDu3erkzwl_WXrqINBm9xWkcyUy53IQOj042dOs,22
3
- maya_umbrella/core.py,sha256=YVMYLsWGZZup1FRgRGaMRj25jbopw8kByOSsjxMaLKg,2512
4
- maya_umbrella/filesystem.py,sha256=U6G4lG4-kbTc2lMVlpg3Umd0u0HVkU2i-AuiEFegDXU,3434
5
- maya_umbrella/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- maya_umbrella/hooks/delete_turtle.py,sha256=EYVXPYi7lBtlUqDFzS0NsftNomZtVBRD-uO705M4KN4,688
7
- maya_umbrella/hooks/delete_unknown_plugin_node.py,sha256=JO0Y2FILrwP7rCW2dOqe_AMhe0OhMtZGHF3w2UAafew,1170
8
- maya_umbrella/hooks/fix_model_panel.py,sha256=cJ65n0_OnFtyKYTnir6S3EluArb8qen1VoIQCpyHtCM,513
9
- maya_umbrella/hooks/fix_on_model_change_3dc.py,sha256=iXXBZNiNYMnp8J0waBKPsEo_4TsTgD8zsV7mDbMM26E,341
10
- maya_umbrella/vaccine.py,sha256=8ekUaa0xaizTOeDzDPtnsyVcj1puP_TUqXg5pdf2bGM,2045
11
- maya_umbrella/vaccines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- maya_umbrella/vaccines/vaccine1.py,sha256=gWz7wl38Uy8s1gRSgcf5g9kN9WrD510zaLuh8FgxYVk,460
13
- maya_umbrella/vaccines/vaccine2.py,sha256=dt0CHfHZHmbUCWF58jBpgAVrIikISdkTFrYMcLr82Dw,2659
14
- maya_umbrella/vaccines/vaccine3.py,sha256=e6sM4JFst_yLkX4wwW7KeHIZkI5i9WoCER1uzUUcPJQ,3756
15
- maya_umbrella-0.3.0.dist-info/LICENSE,sha256=tJf0Pz8q_65AjEkm3872K1cl4jGil28vJO5Ko_LhUqc,1060
16
- maya_umbrella-0.3.0.dist-info/METADATA,sha256=DMn3Ff9evgj3noS6eh1Kh1o2UBeoZRPIljkmKGzVlzA,2818
17
- maya_umbrella-0.3.0.dist-info/WHEEL,sha256=IrRNNNJ-uuL1ggO5qMvT1GGhQVdQU54d6ZpYqEZfEWo,92
18
- maya_umbrella-0.3.0.dist-info/RECORD,,