First checkin of the Python XPCOM bindings.
This commit is contained in:
211
extensions/python/xpcom/server/policy.py
Normal file
211
extensions/python/xpcom/server/policy.py
Normal 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())
|
||||
Reference in New Issue
Block a user