VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/python/client/__init__.py@ 59798

Last change on this file since 59798 was 59798, checked in by vboxsync, 9 years ago

re-applied the Python 3 changes which were backed out in r105674 sans the changes in .cpp

  • Property svn:eol-style set to native
File size: 22.6 KB
Line 
1# ***** BEGIN LICENSE BLOCK *****
2# Version: MPL 1.1/GPL 2.0/LGPL 2.1
3#
4# The contents of this file are subject to the Mozilla Public License Version
5# 1.1 (the "License"); you may not use this file except in compliance with
6# the License. You may obtain a copy of the License at
7# http://www.mozilla.org/MPL/
8#
9# Software distributed under the License is distributed on an "AS IS" basis,
10# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11# for the specific language governing rights and limitations under the
12# License.
13#
14# The Original Code is the Python XPCOM language bindings.
15#
16# The Initial Developer of the Original Code is
17# ActiveState Tool Corp.
18# Portions created by the Initial Developer are Copyright (C) 2000, 2001
19# the Initial Developer. All Rights Reserved.
20#
21# Contributor(s):
22# Mark Hammond <[email protected]> (original author)
23#
24# Alternatively, the contents of this file may be used under the terms of
25# either the GNU General Public License Version 2 or later (the "GPL"), or
26# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27# in which case the provisions of the GPL or the LGPL are applicable instead
28# of those above. If you wish to allow use of your version of this file only
29# under the terms of either the GPL or the LGPL, and not to allow others to
30# use your version of this file under the terms of the MPL, indicate your
31# decision by deleting the provisions above and replace them with the notice
32# and other provisions required by the GPL or the LGPL. If you do not delete
33# the provisions above, a recipient may use your version of this file under
34# the terms of any one of the MPL, the GPL or the LGPL.
35#
36# ***** END LICENSE BLOCK *****
37
38import os
39from types import MethodType
40import logging
41from xpcom import xpt, COMException, nsError, logger
42
43# Suck in stuff from _xpcom we use regularly to prevent a module lookup
44from xpcom._xpcom import IID_nsISupports, IID_nsIClassInfo, \
45 IID_nsISupportsCString, IID_nsISupportsString, \
46 IID_nsISupportsWeakReference, IID_nsIWeakReference, \
47 XPTI_GetInterfaceInfoManager, GetComponentManager, XPTC_InvokeByIndex
48
49# Attribute names we may be __getattr__'d for, but know we don't want to delegate
50# Could maybe just look for startswith("__") but this may screw things for some objects.
51_special_getattr_names = ["__del__", "__len__", "__nonzero__", "__eq__", "__neq__"]
52
53_just_int_interfaces = ["nsISupportsPRInt32", "nsISupportsPRInt16", "nsISupportsPRUint32", "nsISupportsPRUint16", "nsISupportsPRUint8", "nsISupportsPRBool"]
54_just_long_interfaces = ["nsISupportsPRInt64", "nsISupportsPRUint64"]
55_just_float_interfaces = ["nsISupportsDouble", "nsISupportsFloat"]
56# When doing a specific conversion, the order we try the interfaces in.
57_int_interfaces = _just_int_interfaces + _just_float_interfaces
58_long_interfaces = _just_long_interfaces + _just_int_interfaces + _just_float_interfaces
59_float_interfaces = _just_float_interfaces + _just_long_interfaces + _just_int_interfaces
60
61method_template = """
62def %s(self, %s):
63 return XPTC_InvokeByIndex(self._comobj_, %d, (%s, (%s)))
64"""
65def _MakeMethodCode(method):
66 # Build a declaration
67 param_no = 0
68 param_decls = []
69 param_flags = []
70 param_names = []
71 used_default = 0
72 for param in method.params:
73 param_no = param_no + 1
74 param_name = "Param%d" % (param_no,)
75 param_default = ""
76 if not param.hidden_indicator and param.IsIn() and not param.IsDipper():
77 if param.IsOut() or used_default: # If the param is "inout", provide a useful default for the "in" direction.
78 param_default = " = None"
79 used_default = 1 # Once we have used one once, we must for the rest!
80 param_decls.append(param_name + param_default)
81 param_names.append(param_name)
82
83 type_repr = xpt.MakeReprForInvoke(param)
84 param_flags.append( (param.param_flags,) + type_repr )
85 sep = ", "
86 param_decls = sep.join(param_decls)
87 if len(param_names)==1: # Damn tuple reprs.
88 param_names = param_names[0] + ","
89 else:
90 param_names = sep.join(param_names)
91 # A couple of extra newlines make them easier to read for debugging :-)
92 return method_template % (method.name, param_decls, method.method_index, tuple(param_flags), param_names)
93
94# Keyed by IID, each item is a tuple of (methods, getters, setters)
95interface_cache = {}
96# Keyed by [iid][name], each item is an unbound method.
97interface_method_cache = {}
98
99# Keyed by clsid from nsIClassInfo - everything ever queried for the CID.
100contractid_info_cache = {}
101have_shutdown = 0
102
103def _shutdown():
104 interface_cache.clear()
105 interface_method_cache.clear()
106 contractid_info_cache.clear()
107 global have_shutdown
108 have_shutdown = 1
109
110# Fully process the named method, generating method code etc.
111def BuildMethod(method_info, iid):
112 name = method_info.name
113 try:
114 return interface_method_cache[iid][name]
115 except KeyError:
116 pass
117 # Generate it.
118 assert not (method_info.IsSetter() or method_info.IsGetter()), "getters and setters should have been weeded out by now"
119 method_code = _MakeMethodCode(method_info)
120 # Build the method - We only build a function object here
121 # - they are bound to each instance as needed.
122
123## print "Method Code for %s (%s):" % (name, iid)
124## print method_code
125 codeObject = compile(method_code, "<XPCOMObject method '%s'>" % (name,), "exec")
126 # Exec the code object
127 tempNameSpace = {}
128 exec(codeObject, globals(), tempNameSpace)
129 ret = tempNameSpace[name]
130 if iid not in interface_method_cache:
131 interface_method_cache[iid] = {}
132 interface_method_cache[iid][name] = ret
133 return ret
134
135from xpcom.xpcom_consts import XPT_MD_GETTER, XPT_MD_SETTER, XPT_MD_NOTXPCOM, XPT_MD_CTOR, XPT_MD_HIDDEN
136FLAGS_TO_IGNORE = XPT_MD_NOTXPCOM | XPT_MD_CTOR | XPT_MD_HIDDEN
137
138# Pre-process the interface - generate a list of methods, constants etc,
139# but don't actually generate the method code.
140def BuildInterfaceInfo(iid):
141 assert not have_shutdown, "Can't build interface info after a shutdown"
142 ret = interface_cache.get(iid, None)
143 if ret is None:
144 # Build the data for the cache.
145 method_code_blocks = []
146 getters = {}
147 setters = {}
148 method_infos = {}
149
150 interface = xpt.Interface(iid)
151 for m in interface.methods:
152 flags = m.flags
153 if flags & FLAGS_TO_IGNORE == 0:
154 if flags & XPT_MD_SETTER:
155 param_flags = list([(x.param_flags,) + xpt.MakeReprForInvoke(x) for x in m.params])
156 setters[m.name] = m.method_index, param_flags
157 elif flags & XPT_MD_GETTER:
158 param_flags = list([(x.param_flags,) + xpt.MakeReprForInvoke(x) for x in m.params])
159 getters[m.name] = m.method_index, param_flags
160 else:
161 method_infos[m.name] = m
162
163 # Build the constants.
164 constants = {}
165 for c in interface.constants:
166 constants[c.name] = c.value
167 ret = method_infos, getters, setters, constants
168 interface_cache[iid] = ret
169 return ret
170
171class _XPCOMBase:
172 def __cmp__(self, other):
173 try:
174 other = other._comobj_
175 except AttributeError:
176 pass
177 return cmp(self._comobj_, other)
178
179 def __hash__(self):
180 return hash(self._comobj_)
181
182 # The basic rich compare ops for equality
183 def __eq__(self, other):
184 try:
185 other = other._comobj_
186 except AttributeError:
187 pass
188 return self._comobj_ == other
189
190 def __neq__(self, other):
191 try:
192 other = other._comobj_
193 except AttributeError:
194 pass
195 return self._comobj_ != other
196
197 # See if the object support strings.
198 def __str__(self):
199 try:
200 self._comobj_.QueryInterface(IID_nsISupportsCString, 0)
201 return str(self._comobj_)
202 except COMException:
203 return self.__repr__()
204
205 def __unicode__(self):
206 try:
207 prin = self._comobj_.QueryInterface(IID_nsISupportsString)
208 except COMException:
209 return unicode(str(self))
210 return prin.data
211
212 # Try the numeric support.
213 def _do_conversion(self, interface_names, cvt):
214 iim = XPTI_GetInterfaceInfoManager()
215 for interface_name in interface_names:
216 iid = iim.GetInfoForName(interface_name).GetIID()
217 try:
218 prim = self._comobj_.QueryInterface(iid)
219 return cvt(prim.data)
220 except COMException:
221 pass
222 raise ValueError("This object does not support automatic numeric conversion to this type")
223
224 def __int__(self):
225 return self._do_conversion(_int_interfaces, int)
226
227 def __long__(self):
228 return self._do_conversion(_long_interfaces, long)
229
230 def __float__(self):
231 return self._do_conversion(_float_interfaces, float)
232
233class Component(_XPCOMBase):
234 def __init__(self, ob, iid = IID_nsISupports):
235 assert not hasattr(ob, "_comobj_"), "Should be a raw nsIWhatever, not a wrapped one"
236 ob_name = None
237 if not hasattr(ob, "IID"):
238 ob_name = ob
239 cm = GetComponentManager()
240 ob = cm.createInstanceByContractID(ob)
241 assert not hasattr(ob, "_comobj_"), "The created object should be a raw nsIWhatever, not a wrapped one"
242 # Keep a reference to the object in the component too
243 self.__dict__['_comobj_'] = ob
244 # hit __dict__ directly to avoid __setattr__()
245 self.__dict__['_interfaces_'] = {} # keyed by IID
246 self.__dict__['_interface_names_'] = {} # keyed by IID name
247 self.__dict__['_interface_infos_'] = {} # keyed by IID
248 self.__dict__['_name_to_interface_iid_'] = {}
249 self.__dict__['_tried_classinfo_'] = 0
250
251 if ob_name is None:
252 ob_name = "<unknown>"
253 self.__dict__['_object_name_'] = ob_name
254 self.QueryInterface(iid)
255
256 def _build_all_supported_interfaces_(self):
257 # Use nsIClassInfo, but don't do it at object construction to keep perf up.
258 # Only pay the penalty when we really need it.
259 assert not self._tried_classinfo_, "already tried to get the class info."
260 self.__dict__['_tried_classinfo_'] = 1
261 # See if nsIClassInfo is supported.
262 try:
263 classinfo = self._comobj_.QueryInterface(IID_nsIClassInfo, 0)
264 except COMException:
265 classinfo = None
266 if classinfo is not None:
267 try:
268 real_cid = classinfo.contractID
269 except COMException:
270 real_cid = None
271 if real_cid:
272 self.__dict__['_object_name_'] = real_cid
273 contractid_info = contractid_info_cache.get(real_cid)
274 else:
275 contractid_info = None
276 if contractid_info is None:
277 try:
278 interface_infos = classinfo.getInterfaces()
279 except COMException:
280 interface_infos = []
281 for nominated_iid in interface_infos:
282 # Interface may appear twice in the class info list, so check this here.
283 if nominated_iid not in self.__dict__['_interface_infos_']:
284 # Just invoke our QI on the object
285 self.queryInterface(nominated_iid)
286 if real_cid is not None:
287 contractid_info = {}
288 contractid_info['_name_to_interface_iid_'] = self.__dict__['_name_to_interface_iid_']
289 contractid_info['_interface_infos_'] = self.__dict__['_interface_infos_']
290 contractid_info_cache[real_cid] = contractid_info
291 else:
292 for key, val in list(contractid_info.items()):
293 self.__dict__[key].update(val)
294
295 self.__dict__['_com_classinfo_'] = classinfo
296
297 def _remember_interface_info(self, iid):
298 # XXX - there is no good reason to cache this only in each instance
299 # It should be cached at the module level, so we don't need to
300 # rebuild the world for each new object.
301 iis = self.__dict__['_interface_infos_']
302 assert iid not in iis, "Already remembered this interface!"
303 try:
304 method_infos, getters, setters, constants = BuildInterfaceInfo(iid)
305 except COMException as why:
306 # Failing to build an interface info generally isn't a real
307 # problem - its probably just that the interface is non-scriptable.
308 logger.info("Failed to build interface info for %s: %s", iid, why)
309 # Remember the fact we failed.
310 iis[iid] = None
311 return
312
313 # Remember all the names so we can delegate
314 iis[iid] = method_infos, getters, setters, constants
315 names = self.__dict__['_name_to_interface_iid_']
316 for name in list(method_infos.keys()): names[name] = iid
317 for name in list(getters.keys()): names[name] = iid
318 for name in list(setters.keys()): names[name] = iid
319 for name in list(constants.keys()): names[name] = iid
320
321 def QueryInterface(self, iid):
322 if iid in self._interfaces_:
323 assert iid_name in self._interface_names_, "_interfaces_ has the key, but _interface_names_ does not!"
324 return self
325 # Haven't seen this before - do a real QI.
326 if iid not in self._interface_infos_:
327 self._remember_interface_info(iid)
328 iface_info = self._interface_infos_[iid]
329 if iface_info is None:
330 # We have tried, but failed, to get this interface info. Its
331 # unlikely to work later either - its probably non-scriptable.
332 # That means our component wrappers are useless - so just return a
333 # raw nsISupports object with no wrapper.
334 return self._comobj_.QueryInterface(iid, 0)
335
336 raw_iface = self._comobj_.QueryInterface(iid, 0)
337
338 method_infos, getters, setters, constants = iface_info
339 new_interface = _Interface(raw_iface, iid, method_infos,
340 getters, setters, constants)
341 self._interfaces_[iid] = new_interface
342 self._interface_names_[iid.name] = new_interface
343 # As we 'flatten' objects when possible, a QI on an object just
344 # returns ourself - all the methods etc on this interface are
345 # available.
346 return self
347
348 queryInterface = QueryInterface # Alternate name.
349
350 def __getattr__(self, attr):
351 if attr in _special_getattr_names:
352 raise AttributeError(attr)
353 # First allow the interface name to return the "raw" interface
354 interface = self.__dict__['_interface_names_'].get(attr, None)
355 if interface is not None:
356 return interface
357 # See if we know the IID of an interface providing this attribute
358 iid = self.__dict__['_name_to_interface_iid_'].get(attr, None)
359 # This may be first time trying this interface - get the nsIClassInfo
360 if iid is None and not self._tried_classinfo_:
361 self._build_all_supported_interfaces_()
362 iid = self.__dict__['_name_to_interface_iid_'].get(attr, None)
363 # If the request is for an interface name, it may now be
364 # available.
365 interface = self.__dict__['_interface_names_'].get(attr, None)
366 if interface is not None:
367 return interface
368
369 if iid is not None:
370 interface = self.__dict__['_interfaces_'].get(iid, None)
371 if interface is None:
372 self.QueryInterface(iid)
373 interface = self.__dict__['_interfaces_'][iid]
374 return getattr(interface, attr)
375 # Some interfaces may provide this name via "native" support.
376 # Loop over all interfaces, and if found, cache it for next time.
377 for interface in list(self.__dict__['_interfaces_'].values()):
378 try:
379 ret = getattr(interface, attr)
380 self.__dict__['_name_to_interface_iid_'][attr] = interface._iid_
381 return ret
382 except AttributeError:
383 pass
384 raise AttributeError("XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr))
385
386 def __setattr__(self, attr, val):
387 iid = self._name_to_interface_iid_.get(attr, None)
388 # This may be first time trying this interface - get the nsIClassInfo
389 if iid is None and not self._tried_classinfo_:
390 self._build_all_supported_interfaces_()
391 iid = self.__dict__['_name_to_interface_iid_'].get(attr, None)
392 if iid is not None:
393 interface = self._interfaces_.get(iid, None)
394 if interface is None:
395 self.QueryInterface(iid)
396 interface = self.__dict__['_interfaces_'][iid]
397 setattr(interface, attr, val)
398 return
399 raise AttributeError("XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr))
400
401 def _get_classinfo_repr_(self):
402 try:
403 if not self._tried_classinfo_:
404 self._build_all_supported_interfaces_()
405 assert self._tried_classinfo_, "Should have tried the class info by now!"
406 except COMException:
407 # Error building the info - ignore the error, but ensure that
408 # we are flagged as *not* having built, so the error is seen
409 # by the first caller who actually *needs* this to work.
410 self.__dict__['_tried_classinfo_'] = 0
411
412 iface_names = list(self.__dict__['_interface_names_'].keys())
413 try:
414 iface_names.remove("nsISupports")
415 except ValueError:
416 pass
417 iface_names.sort()
418
419 iface_desc = "implementing %s" % (",".join(iface_names),)
420 return iface_desc
421
422 def __repr__(self):
423 # We can advantage from nsIClassInfo - use it.
424 iface_desc = self._get_classinfo_repr_()
425 return "<XPCOM component '%s' (%s)>" % (self._object_name_,iface_desc)
426
427class _Interface(_XPCOMBase):
428 def __init__(self, comobj, iid, method_infos, getters, setters, constants):
429 self.__dict__['_comobj_'] = comobj
430 self.__dict__['_iid_'] = iid
431 self.__dict__['_property_getters_'] = getters
432 self.__dict__['_property_setters_'] = setters
433 self.__dict__['_method_infos_'] = method_infos # method infos waiting to be turned into real methods.
434 self.__dict__['_methods_'] = {} # unbound methods
435 self.__dict__['_object_name_'] = iid.name
436 self.__dict__.update(constants)
437 # We remember the constant names to prevent the user trying to assign to them!
438 self.__dict__['_constant_names_'] = list(constants.keys())
439
440 def __getattr__(self, attr):
441 # Allow the underlying interface to provide a better implementation if desired.
442 if attr in _special_getattr_names:
443 raise AttributeError(attr)
444
445 ret = getattr(self.__dict__['_comobj_'], attr, None)
446 if ret is not None:
447 return ret
448 # Do the function thing first.
449 unbound_method = self.__dict__['_methods_'].get(attr, None)
450 if unbound_method is not None:
451 return MethodType(unbound_method, self)
452
453 getters = self.__dict__['_property_getters_']
454 info = getters.get(attr)
455 if info is not None:
456 method_index, param_infos = info
457 if len(param_infos)!=1: # Only expecting a retval
458 raise RuntimeError("Can't get properties with this many args!")
459 args = ( param_infos, () )
460 return XPTC_InvokeByIndex(self._comobj_, method_index, args)
461
462 # See if we have a method info waiting to be turned into a method.
463 # Do this last as it is a one-off hit.
464 method_info = self.__dict__['_method_infos_'].get(attr, None)
465 if method_info is not None:
466 unbound_method = BuildMethod(method_info, self._iid_)
467 # Cache it locally
468 self.__dict__['_methods_'][attr] = unbound_method
469 return MethodType(unbound_method, self)
470
471 raise AttributeError("XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr))
472
473 def __setattr__(self, attr, val):
474 # If we already have a __dict__ item of that name, and its not one of
475 # our constants, we just directly set it, and leave.
476 if attr in self.__dict__ and attr not in self.__dict__['_constant_names_']:
477 self.__dict__[attr] = val
478 return
479 # Start sniffing for what sort of attribute this might be?
480 setters = self.__dict__['_property_setters_']
481 info = setters.get(attr)
482 if info is None:
483 raise AttributeError("XPCOM component '%s' can not set attribute '%s'" % (self._object_name_, attr))
484 method_index, param_infos = info
485 if len(param_infos)!=1: # Only expecting a single input val
486 raise RuntimeError("Can't set properties with this many args!")
487 real_param_infos = ( param_infos, (val,) )
488 return XPTC_InvokeByIndex(self._comobj_, method_index, real_param_infos)
489
490 def __repr__(self):
491 return "<XPCOM interface '%s'>" % (self._object_name_,)
492
493
494# Called by the _xpcom C++ framework to wrap interfaces up just
495# before they are returned.
496def MakeInterfaceResult(ob, iid):
497 return Component(ob, iid)
498
499class WeakReference:
500 """A weak-reference object. You construct a weak reference by passing
501 any COM object you like. If the object does not support weak
502 refs, you will get a standard NS_NOINTERFACE exception.
503
504 Once you have a weak-reference, you can "call" the object to get
505 back a strong reference. Eg:
506
507 >>> some_ob = components.classes['...']
508 >>> weak_ref = WeakReference(some_ob)
509 >>> new_ob = weak_ref() # new_ob is effectively "some_ob" at this point
510 >>> # EXCEPT: new_ob may be None if some_ob has already died - a
511 >>> # weak reference does not keep the object alive (that is the point)
512
513 You should never hold onto this resulting strong object for a long time,
514 or else you defeat the purpose of the weak-reference.
515 """
516 def __init__(self, ob, iid = None):
517 swr = Component(ob._comobj_, IID_nsISupportsWeakReference)
518 self._comobj_ = Component(swr.GetWeakReference()._comobj_, IID_nsIWeakReference)
519 if iid is None:
520 try:
521 iid = ob.IID
522 except AttributeError:
523 iid = IID_nsISupports
524 self._iid_ = iid
525 def __call__(self, iid = None):
526 if iid is None: iid = self._iid_
527 try:
528 return Component(self._comobj_.QueryReferent(iid)._comobj_, iid)
529 except COMException as details:
530 if details.errno != nsError.NS_ERROR_NULL_POINTER:
531 raise
532 return None
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette