dynamic.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. """Support for dynamic COM client support.
  2. Introduction
  3. Dynamic COM client support is the ability to use a COM server without
  4. prior knowledge of the server. This can be used to talk to almost all
  5. COM servers, including much of MS Office.
  6. In general, you should not use this module directly - see below.
  7. Example
  8. >>> import win32com.client
  9. >>> xl = win32com.client.Dispatch("Excel.Application")
  10. # The line above invokes the functionality of this class.
  11. # xl is now an object we can use to talk to Excel.
  12. >>> xl.Visible = 1 # The Excel window becomes visible.
  13. """
  14. import sys
  15. import traceback
  16. import types
  17. import pythoncom
  18. import winerror
  19. import build
  20. from pywintypes import IIDType
  21. import win32com.client # Needed as code we eval() references it.
  22. debugging=0 # General debugging
  23. debugging_attr=0 # Debugging dynamic attribute lookups.
  24. LCID = 0x0
  25. # These errors generally mean the property or method exists,
  26. # but can't be used in this context - eg, property instead of a method, etc.
  27. # Used to determine if we have a real error or not.
  28. ERRORS_BAD_CONTEXT = [
  29. winerror.DISP_E_MEMBERNOTFOUND,
  30. winerror.DISP_E_BADPARAMCOUNT,
  31. winerror.DISP_E_PARAMNOTOPTIONAL,
  32. winerror.DISP_E_TYPEMISMATCH,
  33. winerror.E_INVALIDARG,
  34. ]
  35. ALL_INVOKE_TYPES = [
  36. pythoncom.INVOKE_PROPERTYGET,
  37. pythoncom.INVOKE_PROPERTYPUT,
  38. pythoncom.INVOKE_PROPERTYPUTREF,
  39. pythoncom.INVOKE_FUNC
  40. ]
  41. def debug_print(*args):
  42. if debugging:
  43. for arg in args:
  44. print arg,
  45. print
  46. def debug_attr_print(*args):
  47. if debugging_attr:
  48. for arg in args:
  49. print arg,
  50. print
  51. # A helper to create method objects on the fly
  52. py3k = sys.version_info > (3,0)
  53. if py3k:
  54. def MakeMethod(func, inst, cls):
  55. return types.MethodType(func, inst) # class not needed in py3k
  56. else:
  57. MakeMethod = types.MethodType # all args used in py2k.
  58. # get the type objects for IDispatch and IUnknown
  59. PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
  60. PyIUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]
  61. if py3k:
  62. _GoodDispatchTypes=(str, IIDType)
  63. else:
  64. _GoodDispatchTypes=(str, IIDType, unicode)
  65. _defaultDispatchItem=build.DispatchItem
  66. def _GetGoodDispatch(IDispatch, clsctx = pythoncom.CLSCTX_SERVER):
  67. # quick return for most common case
  68. if isinstance(IDispatch, PyIDispatchType):
  69. return IDispatch
  70. if isinstance(IDispatch, _GoodDispatchTypes):
  71. try:
  72. IDispatch = pythoncom.connect(IDispatch)
  73. except pythoncom.ole_error:
  74. IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
  75. else:
  76. # may already be a wrapped class.
  77. IDispatch = getattr(IDispatch, "_oleobj_", IDispatch)
  78. return IDispatch
  79. def _GetGoodDispatchAndUserName(IDispatch, userName, clsctx):
  80. # Get a dispatch object, and a 'user name' (ie, the name as
  81. # displayed to the user in repr() etc.
  82. if userName is None:
  83. # Displayed name should be a plain string in py2k, and unicode in py3k
  84. if isinstance(IDispatch, str):
  85. userName = IDispatch
  86. elif not py3k and isinstance(IDispatch, unicode):
  87. # 2to3 converts the above 'unicode' to 'str', but this will never be executed in py3k
  88. userName = IDispatch.encode("ascii", "replace")
  89. ## ??? else userName remains None ???
  90. elif not py3k and isinstance(userName, unicode):
  91. # 2to3 converts the above 'unicode' to 'str', but this will never be executed in py3k
  92. # As above - always a plain string in py2k
  93. userName = userName.encode("ascii", "replace")
  94. else:
  95. userName = str(userName)
  96. return (_GetGoodDispatch(IDispatch, clsctx), userName)
  97. def _GetDescInvokeType(entry, invoke_type):
  98. # determine the wFlags argument passed as input to IDispatch::Invoke
  99. if not entry or not entry.desc: return invoke_type
  100. varkind = entry.desc[4] # from VARDESC struct returned by ITypeComp::Bind
  101. if varkind == pythoncom.VAR_DISPATCH and invoke_type == pythoncom.INVOKE_PROPERTYGET:
  102. return pythoncom.INVOKE_FUNC | invoke_type # DISPATCH_METHOD & DISPATCH_PROPERTYGET can be combined in IDispatch::Invoke
  103. else:
  104. return varkind
  105. def Dispatch(IDispatch, userName = None, createClass = None, typeinfo = None, UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER):
  106. assert UnicodeToString is None, "this is deprecated and will go away"
  107. IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch,userName,clsctx)
  108. if createClass is None:
  109. createClass = CDispatch
  110. lazydata = None
  111. try:
  112. if typeinfo is None:
  113. typeinfo = IDispatch.GetTypeInfo()
  114. if typeinfo is not None:
  115. try:
  116. #try for a typecomp
  117. typecomp = typeinfo.GetTypeComp()
  118. lazydata = typeinfo, typecomp
  119. except pythoncom.com_error:
  120. pass
  121. except pythoncom.com_error:
  122. typeinfo = None
  123. olerepr = MakeOleRepr(IDispatch, typeinfo, lazydata)
  124. return createClass(IDispatch, olerepr, userName, lazydata=lazydata)
  125. def MakeOleRepr(IDispatch, typeinfo, typecomp):
  126. olerepr = None
  127. if typeinfo is not None:
  128. try:
  129. attr = typeinfo.GetTypeAttr()
  130. # If the type info is a special DUAL interface, magically turn it into
  131. # a DISPATCH typeinfo.
  132. if attr[5] == pythoncom.TKIND_INTERFACE and attr[11] & pythoncom.TYPEFLAG_FDUAL:
  133. # Get corresponding Disp interface;
  134. # -1 is a special value which does this for us.
  135. href = typeinfo.GetRefTypeOfImplType(-1);
  136. typeinfo = typeinfo.GetRefTypeInfo(href)
  137. attr = typeinfo.GetTypeAttr()
  138. if typecomp is None:
  139. olerepr = build.DispatchItem(typeinfo, attr, None, 0)
  140. else:
  141. olerepr = build.LazyDispatchItem(attr, None)
  142. except pythoncom.ole_error:
  143. pass
  144. if olerepr is None: olerepr = build.DispatchItem()
  145. return olerepr
  146. def DumbDispatch(IDispatch, userName = None, createClass = None,UnicodeToString=None, clsctx=pythoncom.CLSCTX_SERVER):
  147. "Dispatch with no type info"
  148. assert UnicodeToString is None, "this is deprecated and will go away"
  149. IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch,userName,clsctx)
  150. if createClass is None:
  151. createClass = CDispatch
  152. return createClass(IDispatch, build.DispatchItem(), userName)
  153. class CDispatch:
  154. def __init__(self, IDispatch, olerepr, userName=None, UnicodeToString=None, lazydata=None):
  155. assert UnicodeToString is None, "this is deprecated and will go away"
  156. if userName is None: userName = "<unknown>"
  157. self.__dict__['_oleobj_'] = IDispatch
  158. self.__dict__['_username_'] = userName
  159. self.__dict__['_olerepr_'] = olerepr
  160. self.__dict__['_mapCachedItems_'] = {}
  161. self.__dict__['_builtMethods_'] = {}
  162. self.__dict__['_enum_'] = None
  163. self.__dict__['_unicode_to_string_'] = None
  164. self.__dict__['_lazydata_'] = lazydata
  165. def __call__(self, *args):
  166. "Provide 'default dispatch' COM functionality - allow instance to be called"
  167. if self._olerepr_.defaultDispatchName:
  168. invkind, dispid = self._find_dispatch_type_(self._olerepr_.defaultDispatchName)
  169. else:
  170. invkind, dispid = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, pythoncom.DISPID_VALUE
  171. if invkind is not None:
  172. allArgs = (dispid,LCID,invkind,1) + args
  173. return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
  174. raise TypeError("This dispatch object does not define a default method")
  175. def __nonzero__(self):
  176. return True # ie "if object:" should always be "true" - without this, __len__ is tried.
  177. # _Possibly_ want to defer to __len__ if available, but Im not sure this is
  178. # desirable???
  179. def __repr__(self):
  180. return "<COMObject %s>" % (self._username_)
  181. def __str__(self):
  182. # __str__ is used when the user does "print object", so we gracefully
  183. # fall back to the __repr__ if the object has no default method.
  184. try:
  185. return str(self.__call__())
  186. except pythoncom.com_error, details:
  187. if details.hresult not in ERRORS_BAD_CONTEXT:
  188. raise
  189. return self.__repr__()
  190. # Delegate comparison to the oleobjs, as they know how to do identity.
  191. def __eq__(self, other):
  192. other = getattr(other, "_oleobj_", other)
  193. return self._oleobj_ == other
  194. def __ne__(self, other):
  195. other = getattr(other, "_oleobj_", other)
  196. return self._oleobj_ != other
  197. def __int__(self):
  198. return int(self.__call__())
  199. def __len__(self):
  200. invkind, dispid = self._find_dispatch_type_("Count")
  201. if invkind:
  202. return self._oleobj_.Invoke(dispid, LCID, invkind, 1)
  203. raise TypeError("This dispatch object does not define a Count method")
  204. def _NewEnum(self):
  205. try:
  206. invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
  207. enum = self._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,LCID,invkind,(13, 10),())
  208. except pythoncom.com_error:
  209. return None # no enumerator for this object.
  210. import util
  211. return util.WrapEnum(enum, None)
  212. def __getitem__(self, index): # syver modified
  213. # Improved __getitem__ courtesy Syver Enstad
  214. # Must check _NewEnum before Item, to ensure b/w compat.
  215. if isinstance(index, int):
  216. if self.__dict__['_enum_'] is None:
  217. self.__dict__['_enum_'] = self._NewEnum()
  218. if self.__dict__['_enum_'] is not None:
  219. return self._get_good_object_(self._enum_.__getitem__(index))
  220. # See if we have an "Item" method/property we can use (goes hand in hand with Count() above!)
  221. invkind, dispid = self._find_dispatch_type_("Item")
  222. if invkind is not None:
  223. return self._get_good_object_(self._oleobj_.Invoke(dispid, LCID, invkind, 1, index))
  224. raise TypeError("This object does not support enumeration")
  225. def __setitem__(self, index, *args):
  226. # XXX - todo - We should support calling Item() here too!
  227. # print "__setitem__ with", index, args
  228. if self._olerepr_.defaultDispatchName:
  229. invkind, dispid = self._find_dispatch_type_(self._olerepr_.defaultDispatchName)
  230. else:
  231. invkind, dispid = pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF, pythoncom.DISPID_VALUE
  232. if invkind is not None:
  233. allArgs = (dispid,LCID,invkind,0,index) + args
  234. return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
  235. raise TypeError("This dispatch object does not define a default method")
  236. def _find_dispatch_type_(self, methodName):
  237. if methodName in self._olerepr_.mapFuncs:
  238. item = self._olerepr_.mapFuncs[methodName]
  239. return item.desc[4], item.dispid
  240. if methodName in self._olerepr_.propMapGet:
  241. item = self._olerepr_.propMapGet[methodName]
  242. return item.desc[4], item.dispid
  243. try:
  244. dispid = self._oleobj_.GetIDsOfNames(0,methodName)
  245. except: ### what error?
  246. return None, None
  247. return pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, dispid
  248. def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
  249. result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
  250. return self._get_good_object_(result, user, resultCLSID)
  251. def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
  252. # Given a dispatch object, wrap it in a class
  253. assert UnicodeToString is None, "this is deprecated and will go away"
  254. return Dispatch(ob, userName)
  255. def _get_good_single_object_(self,ob,userName = None, ReturnCLSID=None):
  256. if isinstance(ob, PyIDispatchType):
  257. # make a new instance of (probably this) class.
  258. return self._wrap_dispatch_(ob, userName, ReturnCLSID)
  259. if isinstance(ob, PyIUnknownType):
  260. try:
  261. ob = ob.QueryInterface(pythoncom.IID_IDispatch)
  262. except pythoncom.com_error:
  263. # It is an IUnknown, but not an IDispatch, so just let it through.
  264. return ob
  265. return self._wrap_dispatch_(ob, userName, ReturnCLSID)
  266. return ob
  267. def _get_good_object_(self,ob,userName = None, ReturnCLSID=None):
  268. """Given an object (usually the retval from a method), make it a good object to return.
  269. Basically checks if it is a COM object, and wraps it up.
  270. Also handles the fact that a retval may be a tuple of retvals"""
  271. if ob is None: # Quick exit!
  272. return None
  273. elif isinstance(ob, tuple):
  274. return tuple(map(lambda o, s=self, oun=userName, rc=ReturnCLSID: s._get_good_single_object_(o, oun, rc), ob))
  275. else:
  276. return self._get_good_single_object_(ob)
  277. def _make_method_(self, name):
  278. "Make a method object - Assumes in olerepr funcmap"
  279. methodName = build.MakePublicAttributeName(name) # translate keywords etc.
  280. methodCodeList = self._olerepr_.MakeFuncMethod(self._olerepr_.mapFuncs[name], methodName,0)
  281. methodCode = "\n".join(methodCodeList)
  282. try:
  283. # print "Method code for %s is:\n" % self._username_, methodCode
  284. # self._print_details_()
  285. codeObject = compile(methodCode, "<COMObject %s>" % self._username_,"exec")
  286. # Exec the code object
  287. tempNameSpace = {}
  288. # "Dispatch" in the exec'd code is win32com.client.Dispatch, not ours.
  289. globNameSpace = globals().copy()
  290. globNameSpace["Dispatch"] = win32com.client.Dispatch
  291. exec codeObject in globNameSpace, tempNameSpace # self.__dict__, self.__dict__
  292. name = methodName
  293. # Save the function in map.
  294. fn = self._builtMethods_[name] = tempNameSpace[name]
  295. newMeth = MakeMethod(fn, self, self.__class__)
  296. return newMeth
  297. except:
  298. debug_print("Error building OLE definition for code ", methodCode)
  299. traceback.print_exc()
  300. return None
  301. def _Release_(self):
  302. """Cleanup object - like a close - to force cleanup when you dont
  303. want to rely on Python's reference counting."""
  304. for childCont in self._mapCachedItems_.itervalues():
  305. childCont._Release_()
  306. self._mapCachedItems_ = {}
  307. if self._oleobj_:
  308. self._oleobj_.Release()
  309. self.__dict__['_oleobj_'] = None
  310. if self._olerepr_:
  311. self.__dict__['_olerepr_'] = None
  312. self._enum_ = None
  313. def _proc_(self, name, *args):
  314. """Call the named method as a procedure, rather than function.
  315. Mainly used by Word.Basic, which whinges about such things."""
  316. try:
  317. item = self._olerepr_.mapFuncs[name]
  318. dispId = item.dispid
  319. return self._get_good_object_(self._oleobj_.Invoke(*(dispId, LCID, item.desc[4], 0) + (args) ))
  320. except KeyError:
  321. raise AttributeError(name)
  322. def _print_details_(self):
  323. "Debug routine - dumps what it knows about an object."
  324. print "AxDispatch container",self._username_
  325. try:
  326. print "Methods:"
  327. for method in self._olerepr_.mapFuncs.iterkeys():
  328. print "\t", method
  329. print "Props:"
  330. for prop, entry in self._olerepr_.propMap.iteritems():
  331. print "\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry))
  332. print "Get Props:"
  333. for prop, entry in self._olerepr_.propMapGet.iteritems():
  334. print "\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry))
  335. print "Put Props:"
  336. for prop, entry in self._olerepr_.propMapPut.iteritems():
  337. print "\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry))
  338. except:
  339. traceback.print_exc()
  340. def __LazyMap__(self, attr):
  341. try:
  342. if self._LazyAddAttr_(attr):
  343. debug_attr_print("%s.__LazyMap__(%s) added something" % (self._username_,attr))
  344. return 1
  345. except AttributeError:
  346. return 0
  347. # Using the typecomp, lazily create a new attribute definition.
  348. def _LazyAddAttr_(self,attr):
  349. if self._lazydata_ is None: return 0
  350. res = 0
  351. typeinfo, typecomp = self._lazydata_
  352. olerepr = self._olerepr_
  353. # We need to explicitly check each invoke type individually - simply
  354. # specifying '0' will bind to "any member", which may not be the one
  355. # we are actually after (ie, we may be after prop_get, but returned
  356. # the info for the prop_put.)
  357. for i in ALL_INVOKE_TYPES:
  358. try:
  359. x,t = typecomp.Bind(attr,i)
  360. # Support 'Get' and 'Set' properties - see
  361. # bug 1587023
  362. if x==0 and attr[:3] in ('Set', 'Get'):
  363. x,t = typecomp.Bind(attr[3:], i)
  364. if x==1: #it's a FUNCDESC
  365. r = olerepr._AddFunc_(typeinfo,t,0)
  366. elif x==2: #it's a VARDESC
  367. r = olerepr._AddVar_(typeinfo,t,0)
  368. else: #not found or TYPEDESC/IMPLICITAPP
  369. r=None
  370. if not r is None:
  371. key, map = r[0],r[1]
  372. item = map[key]
  373. if map==olerepr.propMapPut:
  374. olerepr._propMapPutCheck_(key,item)
  375. elif map==olerepr.propMapGet:
  376. olerepr._propMapGetCheck_(key,item)
  377. res = 1
  378. except:
  379. pass
  380. return res
  381. def _FlagAsMethod(self, *methodNames):
  382. """Flag these attribute names as being methods.
  383. Some objects do not correctly differentiate methods and
  384. properties, leading to problems when calling these methods.
  385. Specifically, trying to say: ob.SomeFunc()
  386. may yield an exception "None object is not callable"
  387. In this case, an attempt to fetch the *property*has worked
  388. and returned None, rather than indicating it is really a method.
  389. Calling: ob._FlagAsMethod("SomeFunc")
  390. should then allow this to work.
  391. """
  392. for name in methodNames:
  393. details = build.MapEntry(self.__AttrToID__(name), (name,))
  394. self._olerepr_.mapFuncs[name] = details
  395. def __AttrToID__(self,attr):
  396. debug_attr_print("Calling GetIDsOfNames for property %s in Dispatch container %s" % (attr, self._username_))
  397. return self._oleobj_.GetIDsOfNames(0,attr)
  398. def __getattr__(self, attr):
  399. if attr=='__iter__':
  400. # We can't handle this as a normal method, as if the attribute
  401. # exists, then it must return an iterable object.
  402. try:
  403. invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
  404. enum = self._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,LCID,invkind,(13, 10),())
  405. except pythoncom.com_error:
  406. raise AttributeError("This object can not function as an iterator")
  407. # We must return a callable object.
  408. class Factory:
  409. def __init__(self, ob):
  410. self.ob = ob
  411. def __call__(self):
  412. import win32com.client.util
  413. return win32com.client.util.Iterator(self.ob)
  414. return Factory(enum)
  415. if attr.startswith('_') and attr.endswith('_'): # Fast-track.
  416. raise AttributeError(attr)
  417. # If a known method, create new instance and return.
  418. try:
  419. return MakeMethod(self._builtMethods_[attr], self, self.__class__)
  420. except KeyError:
  421. pass
  422. # XXX - Note that we current are case sensitive in the method.
  423. #debug_attr_print("GetAttr called for %s on DispatchContainer %s" % (attr,self._username_))
  424. # First check if it is in the method map. Note that an actual method
  425. # must not yet exist, (otherwise we would not be here). This
  426. # means we create the actual method object - which also means
  427. # this code will never be asked for that method name again.
  428. if attr in self._olerepr_.mapFuncs:
  429. return self._make_method_(attr)
  430. # Delegate to property maps/cached items
  431. retEntry = None
  432. if self._olerepr_ and self._oleobj_:
  433. # first check general property map, then specific "put" map.
  434. retEntry = self._olerepr_.propMap.get(attr)
  435. if retEntry is None:
  436. retEntry = self._olerepr_.propMapGet.get(attr)
  437. # Not found so far - See what COM says.
  438. if retEntry is None:
  439. try:
  440. if self.__LazyMap__(attr):
  441. if attr in self._olerepr_.mapFuncs: return self._make_method_(attr)
  442. retEntry = self._olerepr_.propMap.get(attr)
  443. if retEntry is None:
  444. retEntry = self._olerepr_.propMapGet.get(attr)
  445. if retEntry is None:
  446. retEntry = build.MapEntry(self.__AttrToID__(attr), (attr,))
  447. except pythoncom.ole_error:
  448. pass # No prop by that name - retEntry remains None.
  449. if not retEntry is None: # see if in my cache
  450. try:
  451. ret = self._mapCachedItems_[retEntry.dispid]
  452. debug_attr_print ("Cached items has attribute!", ret)
  453. return ret
  454. except (KeyError, AttributeError):
  455. debug_attr_print("Attribute %s not in cache" % attr)
  456. # If we are still here, and have a retEntry, get the OLE item
  457. if not retEntry is None:
  458. invoke_type = _GetDescInvokeType(retEntry, pythoncom.INVOKE_PROPERTYGET)
  459. debug_attr_print("Getting property Id 0x%x from OLE object" % retEntry.dispid)
  460. try:
  461. ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1)
  462. except pythoncom.com_error, details:
  463. if details.hresult in ERRORS_BAD_CONTEXT:
  464. # May be a method.
  465. self._olerepr_.mapFuncs[attr] = retEntry
  466. return self._make_method_(attr)
  467. raise
  468. debug_attr_print("OLE returned ", ret)
  469. return self._get_good_object_(ret)
  470. # no where else to look.
  471. raise AttributeError("%s.%s" % (self._username_, attr))
  472. def __setattr__(self, attr, value):
  473. if attr in self.__dict__: # Fast-track - if already in our dict, just make the assignment.
  474. # XXX - should maybe check method map - if someone assigns to a method,
  475. # it could mean something special (not sure what, tho!)
  476. self.__dict__[attr] = value
  477. return
  478. # Allow property assignment.
  479. debug_attr_print("SetAttr called for %s.%s=%s on DispatchContainer" % (self._username_, attr, repr(value)))
  480. if self._olerepr_:
  481. # Check the "general" property map.
  482. if attr in self._olerepr_.propMap:
  483. entry = self._olerepr_.propMap[attr]
  484. invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
  485. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  486. return
  487. # Check the specific "put" map.
  488. if attr in self._olerepr_.propMapPut:
  489. entry = self._olerepr_.propMapPut[attr]
  490. invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
  491. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  492. return
  493. # Try the OLE Object
  494. if self._oleobj_:
  495. if self.__LazyMap__(attr):
  496. # Check the "general" property map.
  497. if attr in self._olerepr_.propMap:
  498. entry = self._olerepr_.propMap[attr]
  499. invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
  500. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  501. return
  502. # Check the specific "put" map.
  503. if attr in self._olerepr_.propMapPut:
  504. entry = self._olerepr_.propMapPut[attr]
  505. invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
  506. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  507. return
  508. try:
  509. entry = build.MapEntry(self.__AttrToID__(attr),(attr,))
  510. except pythoncom.com_error:
  511. # No attribute of that name
  512. entry = None
  513. if entry is not None:
  514. try:
  515. invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
  516. self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
  517. self._olerepr_.propMap[attr] = entry
  518. debug_attr_print("__setattr__ property %s (id=0x%x) in Dispatch container %s" % (attr, entry.dispid, self._username_))
  519. return
  520. except pythoncom.com_error:
  521. pass
  522. raise AttributeError("Property '%s.%s' can not be set." % (self._username_, attr))