VirtualBox

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

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

python/client/init.py: Corrected typo or something (iid_name -> iid.name).

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