First checkin of the Python XPCOM bindings.

git-svn-id: svn://10.0.0.236/trunk@87331 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
markh%activestate.com
2001-02-19 05:24:45 +00:00
parent 0b610ca389
commit abe83923e1
70 changed files with 13648 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
# Copyright (c) 2000-2001 ActiveState Tool Corporation.
# See the file LICENSE.txt for licensing information.
# The xpcom.server package.
from policy import DefaultPolicy
from xpcom import _xpcom
# We define the concept of a single "tracer" object - similar to the single
# Python "trace hook" for debugging. Someone can set
# xpcom.server.tracer to some class/function, and it will be used in place
# of the real xpcom object. Presumably this "trace" object will delegate
# to the real object, but presumably also taking some other action, such
# as calling a profiler or debugger.
tracer = None
# Wrap an instance in an interface (via a policy)
def WrapObject(ob, iid, policy = None):
"""Called by the framework to attempt to wrap
an object in a policy.
If iid is None, it will use the first interface the object indicates it supports.
"""
if policy is None:
policy = DefaultPolicy
if tracer is not None:
ob = tracer(ob)
return _xpcom.WrapObject(policy( ob, iid ), iid)
# Create the main module for the Python loader.
# This is a once only init process, and the returned object
# if used to load all other Python components.
# This means that we keep all factories, modules etc implemented in
# Python!
def NS_GetModule( serviceManager, nsIFile ):
import loader
iid = _xpcom.IID_nsIModule
return WrapObject(loader.MakePythonComponentLoaderModule(serviceManager, nsIFile), iid)

View File

@@ -0,0 +1,24 @@
# Copyright (c) 2000-2001 ActiveState Tool Corporation.
# See the file LICENSE.txt for licensing information.
from xpcom import components
# This class is created by Python components when it
# needs to return an enumerator.
# For example, a component may implement a function:
# nsISimpleEnumerator enumSomething();
# This could could simply say:
# return SimpleEnumerator([something1, something2, something3])
class SimpleEnumerator:
_com_interfaces_ = [components.interfaces.nsISimpleEnumerator]
def __init__(self, data):
self._data = data
self._index = 0
def hasMoreElements(self):
return self._index < len(self._data)
def getNext(self):
self._index = self._index + 1
return self._data[self._index-1]

View File

@@ -0,0 +1,36 @@
# Copyright (c) 2000-2001 ActiveState Tool Corporation.
# See the file LICENSE.txt for licensing information.
# Class factory
#
# Hardly worth its own source file!
import xpcom
from xpcom import components, nsError, _xpcom
class Factory:
_com_interfaces_ = components.interfaces.nsIFactory
# This will only ever be constructed via other Python code,
# so we can have ctor args.
def __init__(self, klass):
self.klass = klass
def createInstance(self, outer, iid):
if outer is not None:
raise xpcom.ServerException(nsError.NS_ERROR_NO_AGGREGATION)
if xpcom.verbose:
print "Python Factory creating", self.klass.__name__
try:
return self.klass()
except:
# An exception here may not be obvious to the user - none
# of their code has been called yet. It can be handy on
# failure to tell the user what class failed!
_xpcom.LogWarning("Creation of class '%r' failed!\nException details follow\n" % (self.klass,))
raise
def lockServer(self, lock):
if xpcom.verbose:
print "Python Factory LockServer called -", lock

View File

@@ -0,0 +1,208 @@
# Copyright (c) 2000-2001 ActiveState Tool Corporation.
# See the file LICENSE.txt for licensing information.
import xpcom
from xpcom import components
import factory
import module
import glob, os, types
from xpcom.client import Component
fileSizeValueName = "FileSize"
lastModValueName = "LastModTimeStamp"
xpcomKeyName = "software/mozilla/XPCOM/components"
# Until we get interface constants.
When_Startup = 0
When_Component = 1
When_Timer = 2
def _has_good_attr(object, attr):
# Actually allows "None" to be specified to disable inherited attributes.
return getattr(object, attr, None) is not None
def FindCOMComponents(py_module):
# For now, just run over all classes looking for likely candidates.
comps = []
for name, object in py_module.__dict__.items():
if type(object)==types.ClassType and \
_has_good_attr(object, "_com_interfaces_") and \
_has_good_attr(object, "_reg_clsid_") and \
_has_good_attr(object, "_reg_contractid_"):
comps.append(object)
return comps
def register_self(compMgr, location, registryLocation, componentType):
pcl = PythonComponentLoader
from xpcom import _xpcom
svc = _xpcom.GetGlobalServiceManager().GetService("@mozilla.org/categorymanager;1", components.interfaces.nsICategoryManager)
svc.addCategoryEntry("component-loader", pcl._reg_component_type_, pcl._reg_contractid_, 1, 1)
class PythonComponentLoader:
_com_interfaces_ = components.interfaces.nsIComponentLoader
_reg_clsid_ = "{63B68B1E-3E62-45f0-98E3-5E0B5797970C}" # Never copy these!
_reg_contractid_ = "moz.pyloader.1"
_reg_desc_ = "Python component loader"
# Optional function which performs additional special registration
# Appears that no special unregistration is needed for ComponentLoaders, hence no unregister function.
_reg_registrar_ = (register_self,None)
# Custom attributes for ComponentLoader registration.
_reg_component_type_ = "script/python"
def __init__(self):
self.com_modules = {} # Keyed by module's FQN as obtained from nsIFile.path
def _getCOMModuleForLocation(self, componentFile):
fqn = componentFile.path
mod = self.com_modules.get(fqn)
if mod is not None:
return mod
import ihooks, sys
base_name = os.path.splitext(os.path.basename(fqn))[0]
loader = ihooks.ModuleLoader()
module_name_in_sys = "component:%s" % (base_name,)
stuff = loader.find_module(base_name, [componentFile.parent.path])
assert stuff is not None, "Couldnt find the module '%s'" % (base_name,)
py_mod = loader.load_module( module_name_in_sys, stuff )
# Make and remember the COM module.
comps = FindCOMComponents(py_mod)
mod = module.Module(comps)
self.com_modules[fqn] = mod
return mod
def getFactory(self, clsid, location, type):
# return the factory
assert type == self._reg_component_type_, "Being asked to create an object not of my type:%s" % (type,)
file_interface = components.manager.specForRegistryLocation(location)
# delegate to the module.
m = self._getCOMModuleForLocation(file_interface)
return m.getClassObject(components.manager, clsid, components.interfaces.nsIFactory)
def init(self, comp_mgr, registry):
# void
registry = registry.QueryInterface(components.interfaces.nsIRegistry)
try:
self.xpcom_registry_key = registry.getSubtree(
components.interfaces.nsIRegistry.Common,
xpcomKeyName)
# If we worked, we can use the registry!
self.registry = registry
except xpcom.Exception, details:
print "Registry failed", details
self.registry = None # no registry ops allowed
self.comp_mgr = comp_mgr
if xpcom.verbose:
print "Python component loader init() called"
# Called when a component of the appropriate type is registered,
# to give the component loader an opportunity to do things like
# annotate the registry and such.
def onRegister (self, clsid, type, className, proId, location, replace, persist):
if xpcom.verbose:
print "Python component loader - onRegister() called"
def autoRegisterComponents (self, when, directory):
directory_path = directory.path
print "Auto-registering all Python components in", directory_path
import traceback
# ToDo - work out the right thing here
# eg - do we recurse?
# - do we support packages?
entries = directory.directoryEntries
while entries.HasMoreElements():
entry = entries.GetNext(components.interfaces.nsIFile)
if os.path.splitext(entry.path)[1]==".py":
try:
self.autoRegisterComponent(when, entry)
except:
print "** Registration of '%s' failed!" % (entry.path,)
traceback.print_exc()
def autoRegisterComponent (self, when, componentFile):
# bool return
reg_loc = components.manager.registryLocationForSpec(componentFile)
# Use the registry to see if we actually need to do anything
if not self._hasChanged(reg_loc, componentFile):
return 1
# Sheesh - it appears we should also use the observer service
# to let the system know of our auto-register progress.
# auto-register via the module.
m = self._getCOMModuleForLocation(componentFile)
m.registerSelf(components.manager, componentFile, reg_loc, self._reg_component_type_)
self._setRegistryInfo(reg_loc, componentFile)
return 1
def autoUnregisterComponent (self, when, componentFile):
# bool return
# auto-unregister via the module.
m = self._getCOMModuleForLocation(componentFile)
reg_loc = components.manager.registryLocationForSpec(componentFile)
try:
m.unregisterSelf(components.manager, componentFile, reg_loc)
finally:
self._removeRegistryInfo( reg_loc, componentFile)
return 1
def registerDeferredComponents (self, when):
# bool return
if xpcom.verbose:
print "Python component loader - registerDeferred() called"
return 0 # no more to register
def unloadAll (self, when):
if xpcom.verbose:
print "Python component loader being asked to unload all components!"
self.registry = None
self.comp_mgr = None
self.com_modules = {}
# Internal Helpers
def _setRegistryInfo(self, registry_location, nsIFile):
if self.registry is None:
return # No registry work allowed.
e_location = self.registry.escapeKey(registry_location, 1)
if e_location is None: # No escaped key needed.
e_location = registry_location
key = self.registry.addSubtreeRaw(self.xpcom_registry_key, e_location)
self.registry.setLongLong(key, lastModValueName, nsIFile.lastModificationDate)
self.registry.setLongLong(key, fileSizeValueName, nsIFile.fileSize)
def _hasChanged(self, registry_location, nsIFile):
if self.registry is None:
# Can't cache in registry - assume it has changed.
return 1
e_location = self.registry.escapeKey(registry_location, 1)
if e_location is None: # No escaped key needed.
e_location = registry_location
try:
key = self.registry.getSubtreeRaw(self.xpcom_registry_key, e_location)
if nsIFile.lastModificationDate != self.registry.getLongLong(key, lastModValueName):
return 1
if nsIFile.fileSize != self.registry.getLongLong(key, fileSizeValueName):
return 1
return 0
except xpcom.Exception, details:
return 1
def _removeRegistryInfo(self, registry_location, nsIFile):
if self.registry is None:
return # No registry work allowed.
e_location = self.registry.escapeKey(registry_location, 1)
if e_location is None: # No escaped key needed.
e_location = registry_location
try:
key = self.registry.removeSubtreeRaw(self.xpcom_registry_key, e_location)
except xpcom.Exception, details:
pass
def MakePythonComponentLoaderModule(serviceManager, nsIFile):
import module
return module.Module( [PythonComponentLoader] )

View File

@@ -0,0 +1,75 @@
# Copyright (c) 2000-2001 ActiveState Tool Corporation.
# See the file LICENSE.txt for licensing information.
from xpcom import components
from xpcom import ServerException, Exception
from xpcom import nsError
import factory
import types
import os
class Module:
_com_interfaces_ = components.interfaces.nsIModule
def __init__(self, comps):
# Build a map of classes we can provide factories for.
c = self.components = {}
for klass in comps:
c[components.ID(klass._reg_clsid_)] = klass
def getClassObject(self, compMgr, clsid, iid):
# Single retval result.
try:
klass = self.components[clsid]
except KeyError:
raise ServerException(nsError.NS_ERROR_FACTORY_NOT_REGISTERED)
# We can ignore the IID - the auto-wrapp process will automatically QI us.
return factory.Factory(klass)
def registerSelf(self, compMgr, location, registryLocation, componentType):
# void function.
for klass in self.components.values():
print "Registering: %s" % (klass.__name__,)
reg_contractid = klass._reg_contractid_
reg_desc = getattr(klass, "_reg_desc_", reg_contractid)
compMgr.registerComponentWithType(klass._reg_clsid_,
reg_desc,
reg_contractid,
location,
registryLocation,
1,
1,
componentType)
# See if this class nominates custom register_self
extra_func = getattr(klass, "_reg_registrar_", (None,None))[0]
if extra_func is not None:
extra_func(compMgr, location, registryLocation, componentType)
print "Registered %d Python components in %s" % (len(self.components),os.path.basename(location.path))
def unregisterSelf(self, compMgr, location, registryLocation):
# void function.
for klass in self.components.values():
ok = 1
try:
compMgr.unregisterComponentSpec(klass._reg_clsid_, location)
except Exception:
ok = 0
# Give the class a bash even if we failed!
extra_func = getattr(klass, "_reg_registrar_", (None,None))[1]
if extra_func is not None:
try:
extra_func(compMgr, location, registryLocation)
except Exception:
ok = 0
if ok:
print "Successfully unregistered", klass.__name__
else:
print "Unregistration of", klass.__name__, "failed. (probably just not already registered)"
def canUnload(self, compMgr):
# single bool result
return 0 # we can never unload!

View File

@@ -0,0 +1,211 @@
# Copyright (c) 2000-2001 ActiveState Tool Corporation.
# See the file LICENSE.txt for licensing information.
from xpcom import xpcom_consts, _xpcom, client, nsError, ServerException, COMException
import xpcom
import traceback
import xpcom.server
import operator
_supports_primitives_map_ = {} # Filled on first use.
def _GetNominatedInterfaces(obj):
ret = getattr(obj, "_com_interfaces_", None)
if ret is None: return None
# See if the user only gave one.
try:
ret[0]
except TypeError:
ret = [ret]
real_ret = []
# For each interface, walk to the root of the interface tree.
iim = _xpcom.XPTI_GetInterfaceInfoManager()
for interface in ret:
try:
interface_info = iim.GetInfoForIID(interface)
except COMException:
# Allow an interface name.
interface_info = iim.GetInfoForName(interface)
real_ret.append(interface_info.GetIID())
parent = interface_info.GetParent()
while parent is not None:
real_ret.append(parent.GetIID())
parent = parent.GetParent()
return real_ret
class DefaultPolicy:
def __init__(self, instance, iid):
self._obj_ = instance
self._nominated_interfaces_ = ni = _GetNominatedInterfaces(instance)
self._iid_ = iid
if ni is None:
raise ValueError, "The object '%r' can not be used as a COM object" % (instance,)
if iid not in ni:
# The object may delegate QI.
try:
delegate_qi = instance._query_interface_
except AttributeError:
delegate_qi = None
# Perform the actual QI and throw away the result - the _real_
# QI performed by the framework will set things right!
if delegate_qi is None or not delegate_qi(iid):
raise ServerException(nsError.NS_ERROR_NO_INTERFACE)
# Stuff for the magic interface conversion.
self._interface_info_ = None
self._interface_iid_map_ = {} # Cache - Indexed by (method_index, param_index)
def _QueryInterface_(self, com_object, iid):
# Framework allows us to return a single boolean integer,
# or a COM object.
if iid in self._nominated_interfaces_:
# We return the underlying object re-wrapped
# in a new gateway - which is desirable, as one gateway should only support
# one interface (this wont affect the users of this policy - we can have as many
# gateways as we like pointing to the same Python objects - the users never
# see what object the call came in from.
# NOTE: We could have simply returned the instance and let the framework
# do the auto-wrap for us - but this way we prevent a round-trip back into Python
# code just for the autowrap.
return xpcom.server.WrapObject(self._obj_, iid)
# See if the instance has a QI
# use lower-case "_query_interface_" as win32com does, and it doesnt really matter.
delegate = getattr(self._obj_, "_query_interface_", None)
if delegate is not None:
# The COM object itself doesnt get passed to the child
# (again, as win32com doesnt). It is rarely needed
# (in win32com, we dont even pass it to the policy, although we have identified
# one place where we should - for marshalling - so I figured I may as well pass it
# to the policy layer here, but no all the way down to the object.
return delegate(iid)
# Finally see if we are being queried for one of the "nsISupports primitives"
if not _supports_primitives_map_:
iim = _xpcom.XPTI_GetInterfaceInfoManager()
for (iid_name, attr, cvt) in _supports_primitives_data_:
special_iid = iim.GetInfoForName(iid_name).GetIID()
_supports_primitives_map_[special_iid] = (attr, cvt)
attr, cvt = _supports_primitives_map_.get(iid, (None,None))
if attr is not None and hasattr(self._obj_, attr):
return xpcom.server.WrapObject(SupportsPrimitive(iid, self._obj_, attr, cvt), iid)
# Out of clever things to try!
return None # We dont support this IID.
def _MakeInterfaceParam_(self, interface, iid, method_index, mi, param_index):
# Wrap a "raw" interface object in a nice object. The result of this
# function will be passed to one of the gateway methods.
if iid is None:
# look up the interface info - this will be true for all xpcom called interfaces.
if self._interface_info_ is None:
import xpcom.xpt
self._interface_info_ = xpcom.xpt.Interface( self._iid_ )
iid = self._interface_iid_map_.get( (method_index, param_index))
if iid is None:
iid = self._interface_info_.GetIIDForParam(method_index, param_index)
self._interface_iid_map_[(method_index, param_index)] = iid
# iid = _xpcom.IID_nsISupports
return client.Interface(interface, iid)
def _CallMethod_(self, com_object, index, info, params):
# print "_CallMethod_", index, info, params
flags, name, param_descs, ret = info
assert ret[1][0] == xpcom_consts.TD_UINT32, "Expected an nsresult (%s)" % (ret,)
if xpcom_consts.XPT_MD_IS_GETTER(flags):
# Look for a function of that name
func = getattr(self._obj_, "get_" + name, None)
if func is None:
assert len(param_descs)==1 and len(params)==0, "Can only handle a single [out] arg for a default getter"
ret = getattr(self._obj_, name) # Let attribute error go here!
else:
ret = func(*params)
return 0, ret
elif xpcom_consts.XPT_MD_IS_SETTER(flags):
# Look for a function of that name
func = getattr(self._obj_, "set_" + name, None)
if func is None:
assert len(param_descs)==1 and len(params)==1, "Can only handle a single [in] arg for a default setter"
setattr(self._obj_, name, params[0]) # Let attribute error go here!
else:
func(*params)
return 0
else:
# A regular method.
func = getattr(self._obj_, name)
return 0, func(*params)
def _doHandleException(self, func_name, exc_info):
exc_val = exc_info[1]
is_server_exception = isinstance(exc_val, ServerException)
if is_server_exception:
if xpcom.verbose:
print "** Information: '%s' raised COM Exception %s" % (func_name, exc_val)
traceback.print_exception(exc_info[0], exc_val, exc_info[2])
print "** Returning nsresult from existing exception", exc_val
return exc_val.errno
# Unhandled exception - always print a warning.
print "** Unhandled exception calling '%s'" % (func_name,)
traceback.print_exception(exc_info[0], exc_val, exc_info[2])
print "** Returning nsresult of NS_ERROR_FAILURE"
return nsError.NS_ERROR_FAILURE
# Called whenever an unhandled Python exception is detected as a result
# of _CallMethod_ - this exception may have been raised during the _CallMethod_
# invocation, or after its return, but when unpacking the results
# eg, type errors, such as a Python integer being used as a string "out" param.
def _CallMethodException_(self, com_object, index, info, params, exc_info):
# Later we may want to have some smart "am I debugging" flags?
# Or maybe just delegate to the actual object - it's probably got the best
# idea what to do with them!
flags, name, param_descs, ret = info
exc_typ, exc_val, exc_tb = exc_info
# use the xpt module to get a better repr for the method.
# But if we fail, ignore it!
try:
import xpcom.xpt
m = xpcom.xpt.Method(info, index, None)
func_repr = m.Describe().lstrip()
except:
func_repr = "%s(%r)" % (name, param_descs)
return self._doHandleException(func_repr, exc_info)
# Called whenever a gateway fails due to anything other than _CallMethod_.
# Really only used for the component loader etc objects, so most
# users should never see exceptions triggered here.
def _GatewayException_(self, name, exc_info):
return self._doHandleException(name, exc_info)
_supports_primitives_data_ = [
("nsISupportsString", "__str__", str),
("nsISupportsWString", "__str__", str),
("nsISupportsPRUint64", "__long__", long),
("nsISupportsPRInt64", "__long__", long),
("nsISupportsPRUint32", "__int__", int),
("nsISupportsPRInt32", "__int__", int),
("nsISupportsPRUint16", "__int__", int),
("nsISupportsPRInt16", "__int__", int),
("nsISupportsPRUint8", "__int__", int),
("nsISupportsPRBool", "__nonzero__", operator.truth),
("nsISupportsDouble", "__float__", float),
("nsISupportsFloat", "__float__", float),
]
# Support for the nsISupports primitives:
class SupportsPrimitive:
_com_interfaces_ = ["nsISupports"]
def __init__(self, iid, base_ob, attr_name, converter):
self.iid = iid
self.base_ob = base_ob
self.attr_name = attr_name
self.converter = converter
def _query_interface_(self, iid):
if iid == self.iid:
return 1
return None
def get_data(self):
method = getattr(self.base_ob, self.attr_name)
val = method()
return self.converter(val)
def set_data(self, val):
raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED)
def toString(self):
return str(self.get_data())