1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
|
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Python XPCOM language bindings.
#
# The Initial Developer of the Original Code is
# ActiveState Tool Corp.
# Portions created by the Initial Developer are Copyright (C) 2001
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Mark Hammond <mhammond@skippinet.com.au> (original author)
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
from xpcom import xpcom_consts, _xpcom, client, nsError, ServerException, COMException
import xpcom
import traceback
import xpcom.server
import operator
import types
IID_nsISupports = _xpcom.IID_nsISupports
IID_nsIVariant = _xpcom.IID_nsIVariant
XPT_MD_IS_GETTER = xpcom_consts.XPT_MD_IS_GETTER
XPT_MD_IS_SETTER = xpcom_consts.XPT_MD_IS_SETTER
VARIANT_INT_TYPES = xpcom_consts.VTYPE_INT8, xpcom_consts.VTYPE_INT16, xpcom_consts.VTYPE_INT32, \
xpcom_consts.VTYPE_UINT8, xpcom_consts.VTYPE_UINT16, xpcom_consts.VTYPE_INT32
VARIANT_LONG_TYPES = xpcom_consts.VTYPE_INT64, xpcom_consts.VTYPE_UINT64
VARIANT_FLOAT_TYPES = xpcom_consts.VTYPE_FLOAT, xpcom_consts.VTYPE_DOUBLE
VARIANT_STRING_TYPES = xpcom_consts.VTYPE_CHAR, xpcom_consts.VTYPE_CHAR_STR, xpcom_consts.VTYPE_STRING_SIZE_IS, \
xpcom_consts.VTYPE_CSTRING
VARIANT_UNICODE_TYPES = xpcom_consts.VTYPE_WCHAR, xpcom_consts.VTYPE_DOMSTRING, xpcom_consts.VTYPE_WSTRING_SIZE_IS, \
xpcom_consts.VTYPE_ASTRING
_supports_primitives_map_ = {} # Filled on first use.
_interface_sequence_types_ = types.TupleType, types.ListType
_string_types_ = types.StringType, types.UnicodeType
XPTI_GetInterfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager
def _GetNominatedInterfaces(obj):
ret = getattr(obj, "_com_interfaces_", None)
if ret is None: return None
# See if the user only gave one.
if type(ret) not in _interface_sequence_types_:
ret = [ret]
real_ret = []
# For each interface, walk to the root of the interface tree.
iim = XPTI_GetInterfaceInfoManager()
for interface in ret:
# Allow interface name or IID.
interface_info = None
if type(interface) in _string_types_:
try:
interface_info = iim.GetInfoForName(interface)
except COMException:
pass
if interface_info is None:
# Allow a real IID
interface_info = iim.GetInfoForIID(interface)
real_ret.append(interface_info.GetIID())
parent = interface_info.GetParent()
while parent is not None:
parent_iid = parent.GetIID()
if parent_iid == IID_nsISupports:
break
real_ret.append(parent_iid)
parent = parent.GetParent()
return real_ret
##
## ClassInfo support
##
## We cache class infos by class
class_info_cache = {}
def GetClassInfoForObject(ob):
if xpcom.server.tracer_unwrap is not None:
ob = xpcom.server.tracer_unwrap(ob)
klass = ob.__class__
ci = class_info_cache.get(klass)
if ci is None:
ci = DefaultClassInfo(klass)
ci = xpcom.server.WrapObject(ci, _xpcom.IID_nsIClassInfo, bWrapClient = 0)
class_info_cache[klass] = ci
return ci
class DefaultClassInfo:
_com_interfaces_ = _xpcom.IID_nsIClassInfo
def __init__(self, klass):
self.klass = klass
self.contractID = getattr(klass, "_reg_contractid_", None)
self.classDescription = getattr(klass, "_reg_desc_", None)
self.classID = getattr(klass, "_reg_clsid_", None)
self.implementationLanguage = 3 # Python - avoid lookups just for this
self.flags = 0 # what to do here??
self.interfaces = None
def get_classID(self):
if self.classID is None:
raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED, "Class '%r' has no class ID" % (self.klass,))
return self.classID
def getInterfaces(self):
if self.interfaces is None:
self.interfaces = _GetNominatedInterfaces(self.klass)
return self.interfaces
def getHelperForLanguage(self, language):
return None # Not sure what to do here.
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,)
# This is really only a check for the user
if __debug__:
if iid != IID_nsISupports and iid not in ni:
# The object may delegate QI.
delegate_qi = getattr(instance, "_query_interface_", 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, bWrapClient = 0)
# Always support nsIClassInfo
if iid == _xpcom.IID_nsIClassInfo:
return GetClassInfoForObject(self._obj_)
# 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, bWrapClient = 0)
# 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
# handle nsIVariant
if iid == IID_nsIVariant:
interface = interface.QueryInterface(iid)
dt = interface.dataType
if dt in VARIANT_INT_TYPES:
return interface.getAsInt32()
if dt in VARIANT_LONG_TYPES:
return interface.getAsInt64()
if dt in VARIANT_FLOAT_TYPES:
return interface.getAsFloat()
if dt in VARIANT_STRING_TYPES:
return interface.getAsStringWithSize()
if dt in VARIANT_UNICODE_TYPES:
return interface.getAsWStringWithSize()
if dt == xpcom_consts.VTYPE_BOOL:
return interface.getAsBool()
if dt == xpcom_consts.VTYPE_INTERFACE:
return interface.getAsISupports()
if dt == xpcom_consts.VTYPE_INTERFACE_IS:
return interface.getAsInterface()
if dt == xpcom_consts.VTYPE_EMPTY or dt == xpcom_consts.VTYPE_VOID:
return None
if dt == xpcom_consts.VTYPE_ARRAY:
return interface.getAsArray()
if dt == xpcom_consts.VTYPE_EMPTY_ARRAY:
return []
if dt == xpcom_consts.VTYPE_ID:
return interface.getAsID()
# all else fails...
print "Warning: nsIVariant type %d not supported - returning a string" % (dt,)
try:
return interface.getAsString()
except COMException:
print "Error: failed to get Variant as a string - returning variant object"
traceback.print_exc()
return interface
return client.Component(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 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 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_ = [
("nsISupportsCString", "__str__", str),
("nsISupportsString", "__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())
def _shutdown():
class_info_cache.clear()
|