Code cleanup: style, unused import
[blender.git] / release / scripts / modules / bpy / ops.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8-80 compliant>
20
21 # for slightly faster access
22 from _bpy import ops as ops_module
23
24 # op_add = ops_module.add
25 op_dir = ops_module.dir
26 op_poll = ops_module.poll
27 op_call = ops_module.call
28 op_as_string = ops_module.as_string
29 op_get_rna = ops_module.get_rna
30 op_get_instance = ops_module.get_instance
31
32
33 class BPyOps(object):
34     """
35     Fake module like class.
36
37      bpy.ops
38     """
39
40     def __getattr__(self, module):
41         """
42         gets a bpy.ops submodule
43         """
44         if module.startswith('__'):
45             raise AttributeError(module)
46         return BPyOpsSubMod(module)
47
48     def __dir__(self):
49
50         submodules = set()
51
52         # add this classes functions
53         for id_name in dir(self.__class__):
54             if not id_name.startswith('__'):
55                 submodules.add(id_name)
56
57         for id_name in op_dir():
58             id_split = id_name.split('_OT_', 1)
59
60             if len(id_split) == 2:
61                 submodules.add(id_split[0].lower())
62             else:
63                 submodules.add(id_split[0])
64
65         return list(submodules)
66
67     def __repr__(self):
68         return "<module like class 'bpy.ops'>"
69
70
71 class BPyOpsSubMod(object):
72     """
73     Utility class to fake submodules.
74
75     eg. bpy.ops.object
76     """
77     __slots__ = ("_module",)
78
79     def __init__(self, module):
80         self._module = module
81
82     def __getattr__(self, func):
83         """
84         gets a bpy.ops.submodule function
85         """
86         if func.startswith('__'):
87             raise AttributeError(func)
88         return BPyOpsSubModOp(self._module, func)
89
90     def __dir__(self):
91
92         functions = set()
93
94         module_upper = self._module.upper()
95
96         for id_name in op_dir():
97             id_split = id_name.split('_OT_', 1)
98             if len(id_split) == 2 and module_upper == id_split[0]:
99                 functions.add(id_split[1])
100
101         return list(functions)
102
103     def __repr__(self):
104         return "<module like class 'bpy.ops.%s'>" % self._module
105
106
107 class BPyOpsSubModOp(object):
108     """
109     Utility class to fake submodule operators.
110
111     eg. bpy.ops.object.somefunc
112     """
113
114     __slots__ = ("_module", "_func")
115
116     def _get_doc(self):
117         return op_as_string(self.idname())
118
119     @staticmethod
120     def _parse_args(args):
121         C_dict = None
122         C_exec = 'EXEC_DEFAULT'
123         C_undo = False
124
125         is_dict = is_exec = is_undo = False
126
127         for i, arg in enumerate(args):
128             if is_dict is False and isinstance(arg, dict):
129                 if is_exec is True or is_undo is True:
130                     raise ValueError("dict arg must come first")
131                 C_dict = arg
132                 is_dict = True
133             elif is_exec is False and isinstance(arg, str):
134                 if is_undo is True:
135                     raise ValueError("string arg must come before the boolean")
136                 C_exec = arg
137                 is_exec = True
138             elif is_undo is False and isinstance(arg, int):
139                 C_undo = arg
140                 is_undo = True
141             else:
142                 raise ValueError("1-3 args execution context is supported")
143
144         return C_dict, C_exec, C_undo
145
146     @staticmethod
147     def _scene_update(context):
148         scene = context.scene
149         if scene:  # None in background mode
150             scene.update()
151         else:
152             import bpy
153             for scene in bpy.data.scenes:
154                 scene.update()
155
156     __doc__ = property(_get_doc)
157
158     def __init__(self, module, func):
159         self._module = module
160         self._func = func
161
162     def poll(self, *args):
163         C_dict, C_exec, C_undo = BPyOpsSubModOp._parse_args(args)
164         return op_poll(self.idname_py(), C_dict, C_exec)
165
166     def idname(self):
167         # submod.foo -> SUBMOD_OT_foo
168         return self._module.upper() + "_OT_" + self._func
169
170     def idname_py(self):
171         # submod.foo -> SUBMOD_OT_foo
172         return self._module + "." + self._func
173
174     def __call__(self, *args, **kw):
175         import bpy
176         context = bpy.context
177
178         # Get the operator from blender
179         wm = context.window_manager
180
181         # run to account for any rna values the user changes.
182         BPyOpsSubModOp._scene_update(context)
183
184         if args:
185             C_dict, C_exec, C_undo = BPyOpsSubModOp._parse_args(args)
186             ret = op_call(self.idname_py(), C_dict, kw, C_exec, C_undo)
187         else:
188             ret = op_call(self.idname_py(), None, kw)
189
190         if 'FINISHED' in ret and context.window_manager == wm:
191             BPyOpsSubModOp._scene_update(context)
192
193         return ret
194
195     def get_rna(self):
196         """Internal function for introspection"""
197         return op_get_rna(self.idname())
198
199     def get_instance(self):
200         """Internal function for introspection"""
201         return op_get_instance(self.idname())
202
203     def __repr__(self):  # useful display, repr(op)
204         # import bpy
205         idname = self.idname()
206         as_string = op_as_string(idname)
207         # XXX You never quite know what you get from bpy.types,
208         # with operators... Operator and OperatorProperties
209         # are shadowing each other, and not in the same way for
210         # native ops and py ones! See T39158.
211         # op_class = getattr(bpy.types, idname)
212         op_class = op_get_rna(idname)
213         descr = op_class.bl_rna.description
214         # XXX, workaround for not registering
215         # every __doc__ to save time on load.
216         if not descr:
217             descr = op_class.__doc__
218             if not descr:
219                 descr = ""
220
221         return "# %s\n%s" % (descr, as_string)
222
223     def __str__(self):  # used for print(...)
224         return ("<function bpy.ops.%s.%s at 0x%x'>" %
225                 (self._module, self._func, id(self)))
226
227 ops_fake_module = BPyOps()