home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 July / CMCD0704.ISO / Software / Shareware / Comunicatii / jyte / jyte.exe / dynamic.py < prev    next >
Text File  |  2004-01-20  |  20KB  |  522 lines

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