Back to index

lightning-sunbird  0.9+nobinonly
__init__.py
Go to the documentation of this file.
00001 # ***** BEGIN LICENSE BLOCK *****
00002 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003 #
00004 # The contents of this file are subject to the Mozilla Public License Version
00005 # 1.1 (the "License"); you may not use this file except in compliance with
00006 # the License. You may obtain a copy of the License at
00007 # http://www.mozilla.org/MPL/
00008 #
00009 # Software distributed under the License is distributed on an "AS IS" basis,
00010 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011 # for the specific language governing rights and limitations under the
00012 # License.
00013 #
00014 # The Original Code is the Python XPCOM language bindings.
00015 #
00016 # The Initial Developer of the Original Code is
00017 # ActiveState Tool Corp.
00018 # Portions created by the Initial Developer are Copyright (C) 2000, 2001
00019 # the Initial Developer. All Rights Reserved.
00020 #
00021 # Contributor(s):
00022 #   Mark Hammond <MarkH@ActiveState.com> (original author)
00023 #
00024 # Alternatively, the contents of this file may be used under the terms of
00025 # either the GNU General Public License Version 2 or later (the "GPL"), or
00026 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027 # in which case the provisions of the GPL or the LGPL are applicable instead
00028 # of those above. If you wish to allow use of your version of this file only
00029 # under the terms of either the GPL or the LGPL, and not to allow others to
00030 # use your version of this file under the terms of the MPL, indicate your
00031 # decision by deleting the provisions above and replace them with the notice
00032 # and other provisions required by the GPL or the LGPL. If you do not delete
00033 # the provisions above, a recipient may use your version of this file under
00034 # the terms of any one of the MPL, the GPL or the LGPL.
00035 #
00036 # ***** END LICENSE BLOCK *****
00037 
00038 import os
00039 import new
00040 from xpcom import xpt, COMException, nsError
00041 
00042 # Suck in stuff from _xpcom we use regularly to prevent a module lookup
00043 from xpcom._xpcom import IID_nsISupports, IID_nsIClassInfo, IID_nsISupportsCString, IID_nsISupportsWeakReference, \
00044         IID_nsIWeakReference, XPTI_GetInterfaceInfoManager, GetComponentManager, XPTC_InvokeByIndex
00045 
00046 # Attribute names we may be __getattr__'d for, but know we don't want to delegate
00047 # Could maybe just look for startswith("__") but this may screw things for some objects.
00048 _special_getattr_names = ["__del__", "__len__", "__nonzero__"]
00049 
00050 _just_int_interfaces = ["nsISupportsPRInt32", "nsISupportsPRInt16", "nsISupportsPRUint32", "nsISupportsPRUint16", "nsISupportsPRUint8", "nsISupportsPRBool"]
00051 _just_long_interfaces = ["nsISupportsPRInt64", "nsISupportsPRUint64"]
00052 _just_float_interfaces = ["nsISupportsDouble", "nsISupportsFloat"]
00053 # When doing a specific conversion, the order we try the interfaces in.
00054 _int_interfaces = _just_int_interfaces + _just_float_interfaces
00055 _long_interfaces = _just_long_interfaces + _just_int_interfaces + _just_float_interfaces
00056 _float_interfaces = _just_float_interfaces + _just_long_interfaces + _just_int_interfaces
00057 
00058 method_template = """
00059 def %s(self, %s):
00060     return XPTC_InvokeByIndex(self._comobj_, %d, (%s, (%s)))
00061 """
00062 def _MakeMethodCode(method):
00063     # Build a declaration
00064     param_no = 0
00065     param_decls = []
00066     param_flags = []
00067     param_names = []
00068     used_default = 0
00069     for param in method.params:
00070         param_no = param_no + 1
00071         param_name = "Param%d" % (param_no,)
00072         param_default = ""
00073         if not param.hidden_indicator and param.IsIn() and not param.IsDipper():
00074             if param.IsOut() or used_default: # If the param is "inout", provide a useful default for the "in" direction.
00075                 param_default = " = None"
00076                 used_default = 1 # Once we have used one once, we must for the rest!
00077             param_decls.append(param_name + param_default)
00078             param_names.append(param_name)
00079 
00080         type_repr = xpt.MakeReprForInvoke(param)
00081         param_flags.append( (param.param_flags,) +  type_repr )
00082     sep = ", "
00083     param_decls = sep.join(param_decls)
00084     if len(param_names)==1: # Damn tuple reprs.
00085         param_names = param_names[0] + ","
00086     else:
00087         param_names = sep.join(param_names)
00088     # A couple of extra newlines make them easier to read for debugging :-)
00089     return method_template % (method.name, param_decls, method.method_index, tuple(param_flags), param_names)
00090 
00091 # Keyed by IID, each item is a tuple of (methods, getters, setters)
00092 interface_cache = {}
00093 # Keyed by [iid][name], each item is an unbound method.
00094 interface_method_cache = {}
00095 
00096 # Keyed by clsid from nsIClassInfo - everything ever queried for the CID.
00097 contractid_info_cache = {}
00098 have_shutdown = 0
00099 
00100 def _shutdown():
00101     interface_cache.clear()
00102     interface_method_cache.clear()
00103     contractid_info_cache.clear()
00104     global have_shutdown
00105     have_shutdown = 1
00106 
00107 # Fully process the named method, generating method code etc.
00108 def BuildMethod(method_info, iid):
00109     name = method_info.name
00110     try:
00111         return interface_method_cache[iid][name]
00112     except KeyError:
00113         pass
00114     # Generate it.
00115     assert not (method_info.IsSetter() or method_info.IsGetter()), "getters and setters should have been weeded out by now"
00116     method_code = _MakeMethodCode(method_info)
00117     # Build the method - We only build a function object here
00118     # - they are bound to each instance as needed.
00119     
00120 ##    print "Method Code for %s (%s):" % (name, iid)
00121 ##    print method_code
00122     codeObject = compile(method_code, "<XPCOMObject method '%s'>" % (name,), "exec")
00123     # Exec the code object
00124     tempNameSpace = {}
00125     exec codeObject in globals(), tempNameSpace
00126     ret = tempNameSpace[name]
00127     if not interface_method_cache.has_key(iid):
00128         interface_method_cache[iid] = {}
00129     interface_method_cache[iid][name] = ret
00130     return ret
00131 
00132 from xpcom.xpcom_consts import XPT_MD_GETTER, XPT_MD_SETTER, XPT_MD_NOTXPCOM, XPT_MD_CTOR, XPT_MD_HIDDEN
00133 FLAGS_TO_IGNORE = XPT_MD_NOTXPCOM | XPT_MD_CTOR | XPT_MD_HIDDEN
00134 
00135 # Pre-process the interface - generate a list of methods, constants etc,
00136 # but don't actually generate the method code.
00137 def BuildInterfaceInfo(iid):
00138     assert not have_shutdown, "Can't build interface info after a shutdown"
00139     ret = interface_cache.get(iid, None)
00140     if ret is None:
00141         # Build the data for the cache.
00142         method_code_blocks = []
00143         getters = {}
00144         setters = {}
00145         method_infos = {}
00146         
00147         interface = xpt.Interface(iid)
00148         for m in interface.methods:
00149             flags = m.flags
00150             if flags & FLAGS_TO_IGNORE == 0:
00151                 if flags & XPT_MD_SETTER:
00152                     param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params)
00153                     setters[m.name] = m.method_index, param_flags
00154                 elif flags & XPT_MD_GETTER:
00155                     param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params)
00156                     getters[m.name] = m.method_index, param_flags
00157                 else:
00158                     method_infos[m.name] = m
00159 
00160         # Build the constants.
00161         constants = {}
00162         for c in interface.constants:
00163             constants[c.name] = c.value
00164         ret = method_infos, getters, setters, constants
00165         interface_cache[iid] = ret
00166     return ret
00167 
00168 class _XPCOMBase:
00169     def __cmp__(self, other):
00170         try:
00171             other = other._comobj_
00172         except AttributeError:
00173             pass
00174         return cmp(self._comobj_, other)
00175 
00176     def __hash__(self):
00177         return hash(self._comobj_)
00178 
00179     # See if the object support strings.
00180     def __str__(self):
00181         try:
00182             self._comobj_.QueryInterface(IID_nsISupportsCString, 0)
00183             return str(self._comobj_)
00184         except COMException:
00185             return self.__repr__()
00186 
00187     # Try the numeric support.
00188     def _do_conversion(self, interface_names, cvt):
00189         iim = XPTI_GetInterfaceInfoManager()
00190         for interface_name in interface_names:
00191             iid = iim.GetInfoForName(interface_name).GetIID()
00192             try:
00193                 prim = self._comobj_.QueryInterface(iid)
00194                 return cvt(prim.data)
00195             except COMException:
00196                 pass
00197         raise ValueError, "This object does not support automatic numeric conversion to this type"
00198 
00199     def __int__(self):
00200         return self._do_conversion(_int_interfaces, int)
00201 
00202     def __long__(self):
00203         return self._do_conversion(_long_interfaces, long)
00204 
00205     def __float__(self):
00206         return self._do_conversion(_float_interfaces, float)
00207     
00208 class Component(_XPCOMBase):
00209     def __init__(self, ob, iid):
00210         assert not hasattr(ob, "_comobj_"), "Should be a raw nsIWhatever, not a wrapped one"
00211         ob_name = None
00212         if not hasattr(ob, "IID"):
00213             ob_name = ob
00214             cm = GetComponentManager()
00215             ob = cm.createInstanceByContractID(ob)
00216             assert not hasattr(ob, "_comobj_"), "The created object should be a raw nsIWhatever, not a wrapped one"
00217         # Keep a reference to the object in the component too
00218         self.__dict__['_comobj_'] = ob
00219         # hit __dict__ directly to avoid __setattr__()
00220         self.__dict__['_interfaces_'] = {} # keyed by IID
00221         self.__dict__['_interface_names_'] = {} # keyed by IID name
00222         self.__dict__['_interface_infos_'] = {} # keyed by IID
00223         self.__dict__['_name_to_interface_name_'] = {}
00224         self.__dict__['_tried_classinfo_'] = 0
00225 
00226         if ob_name is None:
00227             ob_name = "<unknown>"
00228         self.__dict__['_object_name_'] = ob_name
00229         self.QueryInterface(iid)
00230 
00231     def _build_all_supported_interfaces_(self):
00232         # Use nsIClassInfo, but don't do it at object construction to keep perf up.
00233         # Only pay the penalty when we really need it.
00234         assert not self._tried_classinfo_, "already tried to get the class info."
00235         self.__dict__['_tried_classinfo_'] = 1
00236         # See if nsIClassInfo is supported.
00237         try:
00238             classinfo = self._comobj_.QueryInterface(IID_nsIClassInfo, 0)
00239         except COMException:
00240             classinfo = None
00241         if classinfo is not None:
00242             try:
00243                 real_cid = classinfo.contractID
00244             except COMException:
00245                 real_cid = None
00246             if real_cid:
00247                 self.__dict__['_object_name_'] = real_cid
00248                 contractid_info = contractid_info_cache.get(real_cid)
00249             else:
00250                 contractid_info = None
00251             if contractid_info is None:
00252                 try:
00253                     interface_infos = classinfo.getInterfaces()
00254                 except COMException:
00255                     interface_infos = []
00256                 for nominated_iid in interface_infos:
00257                     # Interface may appear twice in the class info list, so check this here.
00258                     if not self.__dict__['_interface_infos_'].has_key(nominated_iid):
00259                         self._remember_interface_info(nominated_iid)
00260                 if real_cid is not None:
00261                     contractid_info = {}
00262                     contractid_info['_name_to_interface_name_'] = self.__dict__['_name_to_interface_name_']
00263                     contractid_info['_interface_infos_'] = self.__dict__['_interface_infos_']
00264                     contractid_info_cache[real_cid] = contractid_info
00265             else:
00266                 for key, val in contractid_info.items():
00267                     self.__dict__[key].update(val)
00268 
00269         self.__dict__['_com_classinfo_'] = classinfo
00270 
00271     def _remember_interface_info(self, iid):
00272         method_infos, getters, setters, constants = BuildInterfaceInfo(iid)
00273         # Remember all the names so we can delegate
00274         assert not self.__dict__['_interface_infos_'].has_key(iid), "Already remembered this interface!"
00275         self.__dict__['_interface_infos_'][iid] = method_infos, getters, setters, constants
00276         interface_name = iid.name
00277         names = self.__dict__['_name_to_interface_name_']
00278         for name in method_infos.keys(): names[name] = interface_name
00279         for name in getters.keys(): names[name] = interface_name
00280         for name in setters.keys(): names[name] = interface_name
00281         for name in constants.keys():  names[name] = interface_name
00282 
00283     def _make_interface_info(self, ob, iid):
00284         interface_infos = self._interface_infos_
00285         assert not self._interfaces_.has_key(iid), "Already have made this interface"
00286         method_infos, getters, setters, constants = interface_infos[iid]
00287         new_interface = _Interface(ob, iid, method_infos, getters, setters, constants)
00288         self._interfaces_[iid] = new_interface
00289         self._interface_names_[iid.name] = new_interface
00290         # No point remembering these.
00291         del interface_infos[iid]
00292 
00293     def QueryInterface(self, iid):
00294         if self._interfaces_.has_key(iid):
00295             assert self._interface_names_.has_key(iid.name), "_interfaces_ has the key, but _interface_names_ does not!"
00296             return self
00297         # Haven't seen this before - do a real QI.
00298         if not self._interface_infos_.has_key(iid):
00299             self._remember_interface_info(iid)
00300         ret = self._comobj_.QueryInterface(iid, 0)
00301 #        print "Component QI for", iid, "yielded", ret
00302         self._make_interface_info(ret, iid)
00303         assert self._interfaces_.has_key(iid) and self._interface_names_.has_key(iid.name), "Making the interface didn't update the maps"
00304         return self
00305 
00306     queryInterface = QueryInterface # Alternate name.
00307 
00308     def __getattr__(self, attr):
00309         if attr in _special_getattr_names:
00310             raise AttributeError, attr
00311         # First allow the interface name to return the "raw" interface
00312         interface = self.__dict__['_interface_names_'].get(attr, None)
00313         if interface is not None:
00314             return interface
00315         interface_name = self.__dict__['_name_to_interface_name_'].get(attr, None)
00316         # This may be first time trying this interface - get the nsIClassInfo
00317         if interface_name is None and not self._tried_classinfo_:
00318             self._build_all_supported_interfaces_()
00319             interface_name = self.__dict__['_name_to_interface_name_'].get(attr, None)
00320             
00321         if interface_name is not None:
00322             interface = self.__dict__['_interface_names_'].get(interface_name, None)
00323             if interface is None:
00324                 iid = XPTI_GetInterfaceInfoManager().GetInfoForName(interface_name).GetIID()
00325                 self.QueryInterface(iid)
00326                 interface = self.__dict__['_interface_names_'][interface_name]
00327             return getattr(interface, attr)
00328         # Some interfaces may provide this name via "native" support.
00329         # Loop over all interfaces, and if found, cache it for next time.
00330         for interface in self.__dict__['_interfaces_'].values():
00331             try:
00332                 ret = getattr(interface, attr)
00333                 self.__dict__['_name_to_interface_name_'][attr] = interface._iid_.name
00334                 return ret
00335             except AttributeError:
00336                 pass
00337         raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
00338         
00339     def __setattr__(self, attr, val):
00340         interface_name = self._name_to_interface_name_.get(attr, None)
00341         # This may be first time trying this interface - get the nsIClassInfo
00342         if interface_name is None and not self._tried_classinfo_:
00343             self._build_all_supported_interfaces_()
00344             interface_name = self.__dict__['_name_to_interface_name_'].get(attr, None)
00345         if interface_name is not None:
00346             interface = self._interface_names_.get(interface_name, None)
00347             if interface is None:
00348                 iid = XPTI_GetInterfaceInfoManager().GetInfoForName(interface_name).GetIID()
00349                 self.QueryInterface(iid)
00350                 interface = self.__dict__['_interface_names_'][interface_name]
00351             setattr(interface, attr, val)
00352             return
00353         raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
00354     def __repr__(self):
00355         # We can advantage from nsIClassInfo - use it.
00356         try:
00357             if not self._tried_classinfo_:
00358                 self._build_all_supported_interfaces_()
00359             assert self._tried_classinfo_, "Should have tried the class info by now!"
00360         except COMException:
00361             # Error building the info - ignore the error, but ensure that
00362             # we are flagged as *not* having built, so the error is seen
00363             # by the first caller who actually *needs* this to work.
00364             self.__dict__['_tried_classinfo_'] = 0
00365         infos = self.__dict__['_interface_infos_']
00366         if infos:
00367             iface_names = ",".join([iid.name for iid in infos.keys()])
00368             iface_desc = " (implementing %s)" % (iface_names,)
00369         else:
00370             iface_desc = " (with no class info)"
00371         return "<XPCOM component '%s'%s>" % (self._object_name_,iface_desc)
00372 
00373 class _Interface(_XPCOMBase):
00374     def __init__(self, comobj, iid, method_infos, getters, setters, constants):
00375         self.__dict__['_comobj_'] = comobj
00376         self.__dict__['_iid_'] = iid
00377         self.__dict__['_property_getters_'] = getters
00378         self.__dict__['_property_setters_'] = setters
00379         self.__dict__['_method_infos_'] = method_infos # method infos waiting to be turned into real methods.
00380         self.__dict__['_methods_'] = {} # unbound methods
00381         self.__dict__['_object_name_'] = iid.name
00382         self.__dict__.update(constants)
00383         # We remember the constant names to prevent the user trying to assign to them!
00384         self.__dict__['_constant_names_'] = constants.keys()
00385 
00386     def __getattr__(self, attr):
00387         # Allow the underlying interface to provide a better implementation if desired.
00388         if attr in _special_getattr_names:
00389             raise AttributeError, attr
00390 
00391         ret = getattr(self.__dict__['_comobj_'], attr, None)
00392         if ret is not None:
00393             return ret
00394         # Do the function thing first.
00395         unbound_method = self.__dict__['_methods_'].get(attr, None)
00396         if unbound_method is not None:
00397             return new.instancemethod(unbound_method, self, self.__class__)
00398 
00399         getters = self.__dict__['_property_getters_']
00400         info = getters.get(attr)
00401         if info is not None:
00402             method_index, param_infos = info
00403             if len(param_infos)!=1: # Only expecting a retval
00404                 raise RuntimeError, "Can't get properties with this many args!"
00405             args = ( param_infos, () )
00406             return XPTC_InvokeByIndex(self, method_index, args)
00407 
00408         # See if we have a method info waiting to be turned into a method.
00409         # Do this last as it is a one-off hit.
00410         method_info = self.__dict__['_method_infos_'].get(attr, None)
00411         if method_info is not None:
00412             unbound_method = BuildMethod(method_info, self._iid_)
00413             # Cache it locally
00414             self.__dict__['_methods_'][attr] = unbound_method
00415             return new.instancemethod(unbound_method, self, self.__class__)
00416 
00417         raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
00418 
00419     def __setattr__(self, attr, val):
00420         # If we already have a __dict__ item of that name, and its not one of
00421         # our constants, we just directly set it, and leave.
00422         if self.__dict__.has_key(attr) and attr not in self.__dict__['_constant_names_']:
00423             self.__dict__[attr] = val
00424             return
00425         # Start sniffing for what sort of attribute this might be?
00426         setters = self.__dict__['_property_setters_']
00427         info = setters.get(attr)
00428         if info is None:
00429             raise AttributeError, "XPCOM component '%s' can not set attribute '%s'" % (self._object_name_, attr)
00430         method_index, param_infos = info
00431         if len(param_infos)!=1: # Only expecting a single input val
00432             raise RuntimeError, "Can't set properties with this many args!"
00433         real_param_infos = ( param_infos, (val,) )
00434         return XPTC_InvokeByIndex(self, method_index, real_param_infos)
00435 
00436     def __repr__(self):
00437         return "<XPCOM interface '%s'>" % (self._object_name_,)
00438 
00439 
00440 # Called by the _xpcom C++ framework to wrap interfaces up just
00441 # before they are returned.
00442 def MakeInterfaceResult(ob, iid):
00443     return Component(ob, iid)
00444 
00445 class WeakReference:
00446     """A weak-reference object.  You construct a weak reference by passing
00447     any COM object you like.  If the object does not support weak
00448     refs, you will get a standard NS_NOINTERFACE exception.
00449 
00450     Once you have a weak-reference, you can "call" the object to get
00451     back a strong reference.  Eg:
00452 
00453     >>> some_ob = components.classes['...']
00454     >>> weak_ref = WeakReference(some_ob)
00455     >>> new_ob = weak_ref() # new_ob is effectively "some_ob" at this point
00456     >>> # EXCEPT: new_ob may be None of some_ob has already died - a
00457     >>> # weak reference does not keep the object alive (that is the point)
00458 
00459     You should never hold onto this resulting strong object for a long time,
00460     or else you defeat the purpose of the weak-reference.
00461     """
00462     def __init__(self, ob, iid = None):
00463         swr = Component(ob._comobj_, IID_nsISupportsWeakReference)
00464         self._comobj_ = Component(swr.GetWeakReference()._comobj_, IID_nsIWeakReference)
00465         if iid is None:
00466             try:
00467                 iid = ob.IID
00468             except AttributeError:
00469                 iid = IID_nsISupports
00470         self._iid_ = iid
00471     def __call__(self, iid = None):
00472         if iid is None: iid = self._iid_
00473         try:
00474             return Component(self._comobj_.QueryReferent(iid)._comobj_, iid)
00475         except COMException, details:
00476             if details.errno != nsError.NS_ERROR_NULL_POINTER:
00477                 raise
00478             return None