~ubuntu-branches/ubuntu/trusty/virtualbox-lts-xenial/trusty-updates

« back to all changes in this revision

Viewing changes to src/libs/xpcom18a4/python/client/__init__.py

  • Committer: Package Import Robot
  • Author(s): Gianfranco Costamagna
  • Date: 2016-02-23 14:28:26 UTC
  • Revision ID: package-import@ubuntu.com-20160223142826-bdu69el2z6wa2a44
Tags: upstream-4.3.36-dfsg
ImportĀ upstreamĀ versionĀ 4.3.36-dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <MarkH@ActiveState.com> (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
 
 
38
import os
 
39
import new
 
40
import logging
 
41
from xpcom import xpt, COMException, nsError, logger
 
42
 
 
43
# Suck in stuff from _xpcom we use regularly to prevent a module lookup
 
44
from 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
 
 
61
method_template = """
 
62
def %s(self, %s):
 
63
    return XPTC_InvokeByIndex(self._comobj_, %d, (%s, (%s)))
 
64
"""
 
65
def _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)
 
95
interface_cache = {}
 
96
# Keyed by [iid][name], each item is an unbound method.
 
97
interface_method_cache = {}
 
98
 
 
99
# Keyed by clsid from nsIClassInfo - everything ever queried for the CID.
 
100
contractid_info_cache = {}
 
101
have_shutdown = 0
 
102
 
 
103
def _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.
 
111
def 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 in globals(), tempNameSpace
 
129
    ret = tempNameSpace[name]
 
130
    if not interface_method_cache.has_key(iid):
 
131
        interface_method_cache[iid] = {}
 
132
    interface_method_cache[iid][name] = ret
 
133
    return ret
 
134
 
 
135
from xpcom.xpcom_consts import XPT_MD_GETTER, XPT_MD_SETTER, XPT_MD_NOTXPCOM, XPT_MD_CTOR, XPT_MD_HIDDEN
 
136
FLAGS_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.
 
140
def 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 = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params)
 
156
                    setters[m.name] = m.method_index, param_flags
 
157
                elif flags & XPT_MD_GETTER:
 
158
                    param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), 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
 
 
171
class _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
    
 
233
class 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 not self.__dict__['_interface_infos_'].has_key(nominated_iid):
 
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 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 not iis.has_key(iid), "Already remembered this interface!"
 
303
        try:
 
304
            method_infos, getters, setters, constants = BuildInterfaceInfo(iid)
 
305
        except COMException, 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 method_infos.keys(): names[name] = iid
 
317
        for name in getters.keys(): names[name] = iid
 
318
        for name in setters.keys(): names[name] = iid
 
319
        for name in constants.keys():  names[name] = iid
 
320
 
 
321
    def QueryInterface(self, iid):
 
322
        if self._interfaces_.has_key(iid):
 
323
            assert self._interface_names_.has_key(iid.name), "_interfaces_ has the key, but _interface_names_ does not!"
 
324
            return self
 
325
        # Haven't seen this before - do a real QI.
 
326
        if not self._interface_infos_.has_key(iid):
 
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 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 = 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
 
 
427
class _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_'] = 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 new.instancemethod(unbound_method, self, self.__class__)
 
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 new.instancemethod(unbound_method, self, self.__class__)
 
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 self.__dict__.has_key(attr) 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.
 
496
def MakeInterfaceResult(ob, iid):
 
497
    return Component(ob, iid)
 
498
 
 
499
class 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, details:
 
530
            if details.errno != nsError.NS_ERROR_NULL_POINTER:
 
531
                raise
 
532
            return None