Back to index

lightning-sunbird  0.9+nobinonly
loader.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
00019 # the Initial Developer. All Rights Reserved.
00020 #
00021 # Contributor(s):
00022 #    Mark Hammond <MarkH@ActiveState.com>
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 xpcom
00039 from xpcom import components
00040 
00041 import module
00042 
00043 import glob, os, types
00044 import traceback
00045 
00046 from xpcom.client import Component
00047 
00048 # Until we get interface constants.
00049 When_Startup = 0
00050 When_Component = 1
00051 When_Timer = 2
00052 
00053 def _has_good_attr(object, attr):
00054     # Actually allows "None" to be specified to disable inherited attributes.
00055     return getattr(object, attr, None) is not None
00056 
00057 def FindCOMComponents(py_module):
00058     # For now, just run over all classes looking for likely candidates.
00059     comps = []
00060     for name, object in py_module.__dict__.items():
00061         if type(object)==types.ClassType and \
00062            _has_good_attr(object, "_com_interfaces_") and \
00063            _has_good_attr(object, "_reg_clsid_") and \
00064            _has_good_attr(object, "_reg_contractid_"):
00065             comps.append(object)
00066     return comps
00067 
00068 def register_self(klass, compMgr, location, registryLocation, componentType):
00069     pcl = PythonComponentLoader
00070     from xpcom import _xpcom
00071     svc = _xpcom.GetServiceManager().getServiceByContractID("@mozilla.org/categorymanager;1", components.interfaces.nsICategoryManager)
00072     svc.addCategoryEntry("component-loader", pcl._reg_component_type_, pcl._reg_contractid_, 1, 1)
00073 
00074 class PythonComponentLoader:
00075     _com_interfaces_ = components.interfaces.nsIComponentLoader
00076     _reg_clsid_ = "{63B68B1E-3E62-45f0-98E3-5E0B5797970C}" # Never copy these!
00077     _reg_contractid_ = "moz.pyloader.1"
00078     _reg_desc_ = "Python component loader"
00079     # Optional function which performs additional special registration
00080     # Appears that no special unregistration is needed for ComponentLoaders, hence no unregister function.
00081     _reg_registrar_ = (register_self,None)
00082     # Custom attributes for ComponentLoader registration.
00083     _reg_component_type_ = "script/python"
00084 
00085     def __init__(self):
00086         self.com_modules = {} # Keyed by module's FQN as obtained from nsIFile.path
00087         self.moduleFactory = module.Module
00088         self.num_modules_this_register = 0
00089 
00090     def _getCOMModuleForLocation(self, componentFile):
00091         fqn = componentFile.path
00092         mod = self.com_modules.get(fqn)
00093         if mod is not None:
00094             return mod
00095         import ihooks, sys
00096         base_name = os.path.splitext(os.path.basename(fqn))[0]
00097         loader = ihooks.ModuleLoader()
00098 
00099         module_name_in_sys = "component:%s" % (base_name,)
00100         stuff = loader.find_module(base_name, [componentFile.parent.path])
00101         assert stuff is not None, "Couldnt find the module '%s'" % (base_name,)
00102         py_mod = loader.load_module( module_name_in_sys, stuff )
00103 
00104         # Make and remember the COM module.
00105         comps = FindCOMComponents(py_mod)
00106         mod = self.moduleFactory(comps)
00107         
00108         self.com_modules[fqn] = mod
00109         return mod
00110         
00111     def getFactory(self, clsid, location, type):
00112         # return the factory
00113         assert type == self._reg_component_type_, "Being asked to create an object not of my type:%s" % (type,)
00114         # FIXME: how to do this without obsolete component manager?
00115         cmo = components.manager.queryInterface(components.interfaces.nsIComponentManagerObsolete)       
00116         file_interface = cmo.specForRegistryLocation(location)
00117         # delegate to the module.
00118         m = self._getCOMModuleForLocation(file_interface)
00119         return m.getClassObject(components.manager, clsid, components.interfaces.nsIFactory)
00120 
00121     def init(self, comp_mgr, registry):
00122         # void
00123         self.comp_mgr = comp_mgr
00124         if xpcom.verbose:
00125             print "Python component loader init() called"
00126 
00127      # Called when a component of the appropriate type is registered,
00128      # to give the component loader an opportunity to do things like
00129      # annotate the registry and such.
00130     def onRegister (self, clsid, type, className, proId, location, replace, persist):
00131         if xpcom.verbose:
00132             print "Python component loader - onRegister() called"
00133 
00134     def autoRegisterComponents (self, when, directory):
00135         directory_path = directory.path
00136         self.num_modules_this_register = 0
00137         if xpcom.verbose:
00138             print "Auto-registering all Python components in", directory_path
00139 
00140         # ToDo - work out the right thing here
00141         # eg - do we recurse?
00142         # - do we support packages?
00143         entries = directory.directoryEntries
00144         while entries.HasMoreElements():
00145             entry = entries.GetNext(components.interfaces.nsIFile)
00146             if os.path.splitext(entry.path)[1]==".py":
00147                 try:
00148                     self.autoRegisterComponent(when, entry)
00149                 # Handle some common user errors
00150                 except xpcom.COMException, details:
00151                     from xpcom import nsError
00152                     # If the interface name does not exist, suppress the traceback
00153                     if details.errno==nsError.NS_ERROR_NO_INTERFACE:
00154                         print "** Registration of '%s' failed\n %s" % (entry.leafName,details.message,)
00155                     else:
00156                         print "** Registration of '%s' failed!" % (entry.leafName,)
00157                         traceback.print_exc()
00158                 except SyntaxError, details:
00159                     # Syntax error in source file - no useful traceback here either.
00160                     print "** Registration of '%s' failed!" % (entry.leafName,)
00161                     traceback.print_exception(SyntaxError, details, None)
00162                 except:
00163                     # All other exceptions get the full traceback.
00164                     print "** Registration of '%s' failed!" % (entry.leafName,)
00165                     traceback.print_exc()
00166 
00167     def autoRegisterComponent (self, when, componentFile):
00168         # bool return
00169         
00170         # Check if we actually need to do anything
00171         modtime = componentFile.lastModifiedTime
00172         loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
00173         if not loader_mgr.hasFileChanged(componentFile, None, modtime):
00174             return 1
00175 
00176         if self.num_modules_this_register == 0:
00177             # New components may have just installed new Python
00178             # modules into the main python directory (including new .pth files)
00179             # So we ask Python to re-process our site directory.
00180             # Note that the pyloader does the equivalent when loading.
00181             try:
00182                 from xpcom import _xpcom
00183                 import site
00184                 NS_XPCOM_CURRENT_PROCESS_DIR="XCurProcD"
00185                 dirname = _xpcom.GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR)
00186                 dirname.append("python")
00187                 site.addsitedir(dirname.path)
00188             except:
00189                 print "PyXPCOM loader failed to process site directory before component registration"
00190                 traceback.print_exc()
00191 
00192         self.num_modules_this_register += 1
00193 
00194         # auto-register via the module.
00195         m = self._getCOMModuleForLocation(componentFile)
00196         m.registerSelf(components.manager, componentFile, None, self._reg_component_type_)
00197         loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
00198         loader_mgr.saveFileInfo(componentFile, None, modtime)
00199         return 1
00200 
00201     def autoUnregisterComponent (self, when, componentFile):
00202         # bool return
00203         # auto-unregister via the module.
00204         m = self._getCOMModuleForLocation(componentFile)
00205         loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
00206         try:
00207             m.unregisterSelf(components.manager, componentFile)
00208         finally:
00209             loader_mgr.removeFileInfo(componentFile, None)
00210         return 1
00211 
00212     def registerDeferredComponents (self, when):
00213         # bool return
00214         if xpcom.verbose:
00215             print "Python component loader - registerDeferred() called"
00216         return 0 # no more to register
00217     def unloadAll (self, when):
00218         if xpcom.verbose:
00219             print "Python component loader being asked to unload all components!"
00220         self.comp_mgr = None
00221         self.com_modules = {}
00222 
00223 def MakePythonComponentLoaderModule(serviceManager, nsIFile):
00224     import module
00225     return module.Module( [PythonComponentLoader] )