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
|
---|
19 | # the Initial Developer. All Rights Reserved.
|
---|
20 | #
|
---|
21 | # Contributor(s):
|
---|
22 | # Mark Hammond <[email protected]>
|
---|
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 xpcom
|
---|
39 | from xpcom import components, logger
|
---|
40 |
|
---|
41 | import module
|
---|
42 |
|
---|
43 | import glob, os, types
|
---|
44 |
|
---|
45 | from xpcom.client import Component
|
---|
46 |
|
---|
47 | # Until we get interface constants.
|
---|
48 | When_Startup = 0
|
---|
49 | When_Component = 1
|
---|
50 | When_Timer = 2
|
---|
51 |
|
---|
52 | def _has_good_attr(object, attr):
|
---|
53 | # Actually allows "None" to be specified to disable inherited attributes.
|
---|
54 | return getattr(object, attr, None) is not None
|
---|
55 |
|
---|
56 | def FindCOMComponents(py_module):
|
---|
57 | # For now, just run over all classes looking for likely candidates.
|
---|
58 | comps = []
|
---|
59 | for name, object in py_module.__dict__.items():
|
---|
60 | try:
|
---|
61 | if (type(object) == types.ClassType or issubclass(object, object)) and \
|
---|
62 | _has_good_attr(object, "_com_interfaces_") and \
|
---|
63 | _has_good_attr(object, "_reg_clsid_") and \
|
---|
64 | _has_good_attr(object, "_reg_contractid_"):
|
---|
65 | comps.append(object)
|
---|
66 | except TypeError:
|
---|
67 | # The issubclass call raises TypeError when the obj is not a class.
|
---|
68 | pass;
|
---|
69 | return comps
|
---|
70 |
|
---|
71 | def register_self(klass, compMgr, location, registryLocation, componentType):
|
---|
72 | pcl = PythonComponentLoader
|
---|
73 | from xpcom import _xpcom
|
---|
74 | svc = _xpcom.GetServiceManager().getServiceByContractID("@mozilla.org/categorymanager;1", components.interfaces.nsICategoryManager)
|
---|
75 | svc.addCategoryEntry("component-loader", pcl._reg_component_type_, pcl._reg_contractid_, 1, 1)
|
---|
76 |
|
---|
77 | class PythonComponentLoader:
|
---|
78 | _com_interfaces_ = components.interfaces.nsIComponentLoader
|
---|
79 | _reg_clsid_ = "{63B68B1E-3E62-45f0-98E3-5E0B5797970C}" # Never copy these!
|
---|
80 | _reg_contractid_ = "moz.pyloader.1"
|
---|
81 | _reg_desc_ = "Python component loader"
|
---|
82 | # Optional function which performs additional special registration
|
---|
83 | # Appears that no special unregistration is needed for ComponentLoaders, hence no unregister function.
|
---|
84 | _reg_registrar_ = (register_self,None)
|
---|
85 | # Custom attributes for ComponentLoader registration.
|
---|
86 | _reg_component_type_ = "script/python"
|
---|
87 |
|
---|
88 | def __init__(self):
|
---|
89 | self.com_modules = {} # Keyed by module's FQN as obtained from nsIFile.path
|
---|
90 | self.moduleFactory = module.Module
|
---|
91 | self.num_modules_this_register = 0
|
---|
92 |
|
---|
93 | def _getCOMModuleForLocation(self, componentFile):
|
---|
94 | fqn = componentFile.path
|
---|
95 | mod = self.com_modules.get(fqn)
|
---|
96 | if mod is not None:
|
---|
97 | return mod
|
---|
98 | import ihooks, sys
|
---|
99 | base_name = os.path.splitext(os.path.basename(fqn))[0]
|
---|
100 | loader = ihooks.ModuleLoader()
|
---|
101 |
|
---|
102 | module_name_in_sys = "component:%s" % (base_name,)
|
---|
103 | stuff = loader.find_module(base_name, [componentFile.parent.path])
|
---|
104 | assert stuff is not None, "Couldnt find the module '%s'" % (base_name,)
|
---|
105 | py_mod = loader.load_module( module_name_in_sys, stuff )
|
---|
106 |
|
---|
107 | # Make and remember the COM module.
|
---|
108 | comps = FindCOMComponents(py_mod)
|
---|
109 | mod = self.moduleFactory(comps)
|
---|
110 |
|
---|
111 | self.com_modules[fqn] = mod
|
---|
112 | return mod
|
---|
113 |
|
---|
114 | def getFactory(self, clsid, location, type):
|
---|
115 | # return the factory
|
---|
116 | assert type == self._reg_component_type_, "Being asked to create an object not of my type:%s" % (type,)
|
---|
117 | # FIXME: how to do this without obsolete component manager?
|
---|
118 | cmo = components.manager.queryInterface(components.interfaces.nsIComponentManagerObsolete)
|
---|
119 | file_interface = cmo.specForRegistryLocation(location)
|
---|
120 | # delegate to the module.
|
---|
121 | m = self._getCOMModuleForLocation(file_interface)
|
---|
122 | return m.getClassObject(components.manager, clsid, components.interfaces.nsIFactory)
|
---|
123 |
|
---|
124 | def init(self, comp_mgr, registry):
|
---|
125 | # void
|
---|
126 | self.comp_mgr = comp_mgr
|
---|
127 | logger.debug("Python component loader init() called")
|
---|
128 |
|
---|
129 | # Called when a component of the appropriate type is registered,
|
---|
130 | # to give the component loader an opportunity to do things like
|
---|
131 | # annotate the registry and such.
|
---|
132 | def onRegister (self, clsid, type, className, proId, location, replace, persist):
|
---|
133 | logger.debug("Python component loader - onRegister() called")
|
---|
134 |
|
---|
135 | def autoRegisterComponents (self, when, directory):
|
---|
136 | directory_path = directory.path
|
---|
137 | self.num_modules_this_register = 0
|
---|
138 | logger.debug("Auto-registering all Python components in '%s'", directory_path)
|
---|
139 |
|
---|
140 | # ToDo - work out the right thing here
|
---|
141 | # eg - do we recurse?
|
---|
142 | # - do we support packages?
|
---|
143 | entries = directory.directoryEntries
|
---|
144 | while entries.HasMoreElements():
|
---|
145 | entry = entries.GetNext(components.interfaces.nsIFile)
|
---|
146 | if os.path.splitext(entry.path)[1]==".py":
|
---|
147 | try:
|
---|
148 | self.autoRegisterComponent(when, entry)
|
---|
149 | # Handle some common user errors
|
---|
150 | except xpcom.COMException, details:
|
---|
151 | from xpcom import nsError
|
---|
152 | # If the interface name does not exist, suppress the traceback
|
---|
153 | if details.errno==nsError.NS_ERROR_NO_INTERFACE:
|
---|
154 | logger.error("Registration of '%s' failed\n %s",
|
---|
155 | entry.leafName, details.message)
|
---|
156 | else:
|
---|
157 | logger.exception("Registration of '%s' failed!", entry.leafName)
|
---|
158 | except SyntaxError, details:
|
---|
159 | # Syntax error in source file - no useful traceback here either.
|
---|
160 | logger.error("Registration of '%s' failed\n %s",
|
---|
161 | entry.leafName, details)
|
---|
162 | except:
|
---|
163 | # All other exceptions get the full traceback.
|
---|
164 | logger.exception("Registration of '%s' failed.", entry.leafName)
|
---|
165 |
|
---|
166 | def autoRegisterComponent (self, when, componentFile):
|
---|
167 | # bool return
|
---|
168 |
|
---|
169 | # Check if we actually need to do anything
|
---|
170 | modtime = componentFile.lastModifiedTime
|
---|
171 | loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
|
---|
172 | if not loader_mgr.hasFileChanged(componentFile, None, modtime):
|
---|
173 | return 1
|
---|
174 |
|
---|
175 | if self.num_modules_this_register == 0:
|
---|
176 | # New components may have just installed new Python
|
---|
177 | # modules into the main python directory (including new .pth files)
|
---|
178 | # So we ask Python to re-process our site directory.
|
---|
179 | # Note that the pyloader does the equivalent when loading.
|
---|
180 | try:
|
---|
181 | from xpcom import _xpcom
|
---|
182 | import site
|
---|
183 | NS_XPCOM_CURRENT_PROCESS_DIR="XCurProcD"
|
---|
184 | dirname = _xpcom.GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR)
|
---|
185 | dirname.append("python")
|
---|
186 | site.addsitedir(dirname.path)
|
---|
187 | except:
|
---|
188 | logger.exception("PyXPCOM loader failed to process site directory before component registration")
|
---|
189 |
|
---|
190 | self.num_modules_this_register += 1
|
---|
191 |
|
---|
192 | # auto-register via the module.
|
---|
193 | m = self._getCOMModuleForLocation(componentFile)
|
---|
194 | m.registerSelf(components.manager, componentFile, None, self._reg_component_type_)
|
---|
195 | loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
|
---|
196 | loader_mgr.saveFileInfo(componentFile, None, modtime)
|
---|
197 | return 1
|
---|
198 |
|
---|
199 | def autoUnregisterComponent (self, when, componentFile):
|
---|
200 | # bool return
|
---|
201 | # auto-unregister via the module.
|
---|
202 | m = self._getCOMModuleForLocation(componentFile)
|
---|
203 | loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
|
---|
204 | try:
|
---|
205 | m.unregisterSelf(components.manager, componentFile)
|
---|
206 | finally:
|
---|
207 | loader_mgr.removeFileInfo(componentFile, None)
|
---|
208 | return 1
|
---|
209 |
|
---|
210 | def registerDeferredComponents (self, when):
|
---|
211 | # bool return
|
---|
212 | logger.debug("Python component loader - registerDeferred() called")
|
---|
213 | return 0 # no more to register
|
---|
214 |
|
---|
215 | def unloadAll (self, when):
|
---|
216 | # This is called at shutdown time - don't get too upset if an error
|
---|
217 | # results from logging due to the logfile being closed
|
---|
218 | try:
|
---|
219 | logger.debug("Python component loader being asked to unload all components!")
|
---|
220 | except:
|
---|
221 | # Evil blank except, but restricting to just catching IOError
|
---|
222 | # failure means custom logs could still screw us
|
---|
223 | pass
|
---|
224 | self.comp_mgr = None
|
---|
225 | self.com_modules = {}
|
---|
226 |
|
---|
227 | def MakePythonComponentLoaderModule(serviceManager, nsIFile):
|
---|
228 | import module
|
---|
229 | return module.Module( [PythonComponentLoader] )
|
---|