home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activepython / ActivePython-2.1.1.msi / Python21_win32com_client_build.py < prev    next >
Encoding:
Python Source  |  2001-07-26  |  20.6 KB  |  568 lines

  1. """Contains knowledge to build a COM object definition.
  2.  
  3. This module is used by both the @dynamic@ and @makepy@ modules to build
  4. all knowledge of a COM object.
  5.  
  6. This module contains classes which contain the actual knowledge of the object.
  7. This include parameter and return type information, the COM dispid and CLSID, etc.
  8.  
  9. Other modules may use this information to generate .py files, use the information
  10. dynamically, or possibly even generate .html documentation for objects.
  11. """
  12.  
  13. #
  14. # NOTES: DispatchItem and MapEntry used by dynamic.py.
  15. #        the rest is used by makepy.py
  16. #
  17. #        OleItem, DispatchItem, MapEntry, BuildCallList() is used by makepy
  18.  
  19. import sys
  20. import string
  21. import types
  22. from keyword import iskeyword
  23. from win32com.client import NeedUnicodeConversions
  24.  
  25. import pythoncom
  26. from pywintypes import UnicodeType
  27.  
  28. error = "PythonCOM.Client.Build error"
  29. class NotSupportedException(Exception): pass # Raised when we cant support a param type.
  30. DropIndirection="DropIndirection"
  31.  
  32. NoTranslateTypes = [
  33.     pythoncom.VT_BOOL,          pythoncom.VT_CLSID,        pythoncom.VT_CY,
  34.     pythoncom.VT_DATE,          pythoncom.VT_DECIMAL,       pythoncom.VT_EMPTY,
  35.     pythoncom.VT_ERROR,         pythoncom.VT_FILETIME,     pythoncom.VT_HRESULT,
  36.     pythoncom.VT_I1,            pythoncom.VT_I2,           pythoncom.VT_I4,
  37.     pythoncom.VT_I8,            pythoncom.VT_INT,          pythoncom.VT_NULL,
  38.     pythoncom.VT_R4,            pythoncom.VT_R8,           pythoncom.VT_NULL,
  39.     pythoncom.VT_STREAM,
  40.     pythoncom.VT_UI1,            pythoncom.VT_UI2,           pythoncom.VT_UI4,
  41.     pythoncom.VT_UI8,            pythoncom.VT_UINT,          pythoncom.VT_VOID,
  42.     pythoncom.VT_UNKNOWN,
  43. ]
  44.  
  45. NoTranslateMap = {}
  46. for v in NoTranslateTypes:
  47.     NoTranslateMap[v] = None
  48.  
  49. class MapEntry:
  50.     "Simple holder for named attibutes - items in a map."
  51.     def __init__(self, desc_or_id, names=None, doc=None, resultCLSID=pythoncom.IID_NULL, resultDoc = None, hidden=0):
  52.         if type(desc_or_id)==type(0):
  53.             self.dispid = desc_or_id
  54.             self.desc = None
  55.         else:
  56.             self.dispid = desc_or_id[0]
  57.             self.desc = desc_or_id
  58.  
  59.         self.names = names
  60.         self.doc = doc
  61.         self.resultCLSID = resultCLSID
  62.         self.resultDocumentation = resultDoc
  63.         self.wasProperty = 0 # Have I been transformed into a function so I can pass args?
  64.         self.hidden = hidden
  65.     def GetResultCLSID(self):
  66.         rc = self.resultCLSID
  67.         if rc == pythoncom.IID_NULL: return None
  68.         return rc
  69.     # Return a string, suitable for output - either "'{...}'" or "None"
  70.     def GetResultCLSIDStr(self):
  71.         rc = self.GetResultCLSID()
  72.         if rc is None: return "None"
  73.         return repr(str(rc)) # Convert the IID object to a string, then to a string in a string.
  74.  
  75.     def GetResultName(self):
  76.         if self.resultDocumentation is None:
  77.             return None
  78.         return self.resultDocumentation[0]
  79.  
  80. class OleItem:
  81.   typename = "OleItem"
  82.  
  83.   def __init__(self, doc=None):
  84.     self.doc = doc
  85.     if self.doc:
  86.         self.python_name = MakePublicAttributeName(self.doc[0])
  87.     else:
  88.         self.python_name = None
  89.     self.bWritten = 0
  90.     self.bIsDispatch = 0
  91.     self.bIsSink = 0
  92.     self.clsid = None
  93.  
  94. class DispatchItem(OleItem):
  95.     typename = "DispatchItem"
  96.  
  97.     def __init__(self, typeinfo=None, attr=None, doc=None, bForUser=1):
  98.         OleItem.__init__(self,doc)
  99.         self.propMap = {}
  100.         self.propMapGet = {}
  101.         self.propMapPut = {}
  102.         self.mapFuncs = {}
  103.         self.defaultDispatchName = None
  104.         self.hidden = 0
  105.  
  106.         if typeinfo:
  107.             self.Build(typeinfo, attr, bForUser)
  108.  
  109.     def _propMapPutCheck_(self,key,item):
  110.         ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
  111.         if ins>1: # if a Put property takes more than 1 arg:
  112.             if opts+1==ins or ins==item.desc[6]+1:
  113.                 newKey = "Set" + key
  114.                 deleteExisting = 0 # This one is still OK
  115.             else:
  116.                 deleteExisting = 1 # No good to us
  117.                 if self.mapFuncs.has_key(key) or self.propMapGet.has_key(key):
  118.                     newKey = "Set" + key
  119.                 else:
  120.                     newKey = key
  121.             item.wasProperty = 1
  122.             self.mapFuncs[newKey] = item
  123.             if deleteExisting:
  124.                 del self.propMapPut[key]
  125.  
  126.     def _propMapGetCheck_(self,key,item):
  127.         ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
  128.         if ins > 0: # if a Get property takes _any_ in args:
  129.             if item.desc[6]==ins or ins==opts:
  130.                 newKey = "Get" + key
  131.                 deleteExisting = 0 # This one is still OK
  132.             else:
  133.                 deleteExisting = 1 # No good to us
  134.                 if self.mapFuncs.has_key(key):
  135.                     newKey = "Get" + key
  136.                 else:
  137.                     newKey = key
  138.             item.wasProperty = 1
  139.             self.mapFuncs[newKey] = item
  140.             if deleteExisting:
  141.                 del self.propMapGet[key]
  142.  
  143.     def    _AddFunc_(self,typeinfo,fdesc,bForUser):
  144.         id = fdesc.memid
  145.         funcflags = fdesc.wFuncFlags
  146.  
  147.         # skip [restricted] methods, unless it is the
  148.         # enumerator (which, being part of the "system",
  149.         # we know about and can use)
  150.         if funcflags & pythoncom.FUNCFLAG_FRESTRICTED and \
  151.             id != pythoncom.DISPID_NEWENUM:
  152.             return None
  153.  
  154.         try:
  155.             names = typeinfo.GetNames(id)
  156.             name=names[0]
  157.         except pythoncom.ole_error:
  158.             name = ""
  159.             names = None
  160.  
  161.         doc = None
  162.         try:
  163.             if bForUser:
  164.                 doc = typeinfo.GetDocumentation(id)
  165.         except pythoncom.ole_error:
  166.             pass
  167.  
  168.         if id==0 and name:
  169.             self.defaultDispatchName = name
  170.  
  171.         invkind = fdesc.invkind
  172.  
  173.         # We need to translate any Alias', Enums, structs etc in result and args
  174.         typerepr, flag, defval = fdesc.rettype
  175. #        sys.stderr.write("%s result - %s -> " % (name, typerepr))
  176.         typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
  177. #        sys.stderr.write("%s\n" % (typerepr,))
  178.         fdesc.rettype = typerepr, flag, defval
  179.         # Translate any Alias or Enums in argument list.
  180.         argList = []
  181.         for argDesc in fdesc.args:
  182.             typerepr, flag, defval = argDesc
  183. #            sys.stderr.write("%s arg - %s -> " % (name, typerepr))
  184.             argDesc = _ResolveType(typerepr, typeinfo)[0], flag, defval
  185. #            sys.stderr.write("%s\n" % (argDesc[0],))
  186.             argList.append(argDesc)
  187.         fdesc.args = tuple(argList)
  188.  
  189.         hidden = (funcflags & pythoncom.FUNCFLAG_FHIDDEN) != 0
  190.         if id == pythoncom.DISPID_NEWENUM:
  191.             map = self.mapFuncs
  192.         elif invkind == pythoncom.INVOKE_PROPERTYGET:
  193.             map = self.propMapGet
  194.         # This is not the best solution, but I dont think there is
  195.         # one without specific "set" syntax.
  196.         # If there is a single PUT or PUTREF, it will function as a property.
  197.         # If there are both, then the PUT remains a property, and the PUTREF
  198.         # gets transformed into a function.
  199.         # (in vb, PUT=="obj=other_obj", PUTREF="set obj=other_obj
  200.         elif invkind in (pythoncom.INVOKE_PROPERTYPUT, pythoncom.INVOKE_PROPERTYPUTREF):
  201.             # Special case
  202.             existing = self.propMapPut.get(name, None)
  203.             if existing is not None:
  204.                 if existing.desc[4]==pythoncom.INVOKE_PROPERTYPUT: # Keep this one
  205.                     map = self.mapFuncs
  206.                     name = "Set"+name
  207.                 else: # Existing becomes a func.
  208.                     existing.wasProperty = 1
  209.                     self.mapFuncs["Set"+name]=existing
  210.                     map = self.propMapPut # existing gets overwritten below.
  211.             else:
  212.                 map = self.propMapPut # first time weve seen it.
  213.  
  214.         elif invkind == pythoncom.INVOKE_FUNC:
  215.             map = self.mapFuncs
  216.         else:
  217.             map = None
  218.         if not map is None: 
  219. #                if map.has_key(name):
  220. #                    sys.stderr.write("Warning - overwriting existing method/attribute %s\n" % name)
  221.             map[name] = MapEntry(tuple(fdesc), names, doc, resultCLSID, resultDoc, hidden)
  222.             # any methods that can't be reached via DISPATCH we return None
  223.             # for, so dynamic dispatch doesnt see it.
  224.             if fdesc.funckind != pythoncom.FUNC_DISPATCH:
  225.                 return None
  226.             return (name,map)
  227.         return None
  228.  
  229.     def _AddVar_(self,typeinfo,fdesc,bForUser):
  230.         ### need pythoncom.VARFLAG_FRESTRICTED ...
  231.         ### then check it
  232.  
  233.         if fdesc.varkind == pythoncom.VAR_DISPATCH:
  234.             id = fdesc.memid
  235.             names = typeinfo.GetNames(id)
  236.             # Translate any Alias or Enums in result.
  237.             typerepr, flags, defval = fdesc.elemdescVar
  238.             typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
  239.             fdesc.elemdescVar = typerepr, flags, defval
  240.             doc = None
  241.             try:
  242.                 if bForUser: doc = typeinfo.GetDocumentation(id)
  243.             except pythoncom.ole_error:
  244.                 pass
  245.  
  246.             # handle the enumerator specially
  247.             if id == pythoncom.DISPID_NEWENUM:
  248.                 map = self.mapFuncs
  249.                 ### hack together a minimal FUNCDESC
  250.                 fdesc = (fdesc[0], None, None, None, pythoncom.INVOKE_PROPERTYGET, )
  251.             else:
  252.                 map = self.propMap
  253.             # Check if the element is hidden.
  254.             hidden = 0
  255.             if hasattr(fdesc,"wVarFlags"):
  256.                 hidden = (fdesc.wVarFlags & 0x40) != 0 # VARFLAG_FHIDDEN
  257.             map[names[0]] = MapEntry(tuple(fdesc), names, doc, resultCLSID, resultDoc, hidden)
  258.             return (names[0],map)
  259.         else:
  260.             return None
  261.  
  262.     def Build(self, typeinfo, attr, bForUser = 1):
  263.         self.clsid = attr[0]
  264.         self.bIsDispatch = (attr.wTypeFlags & pythoncom.TYPEFLAG_FDISPATCHABLE) != 0
  265.         if typeinfo is None: return
  266.         # Loop over all methods
  267.         for j in xrange(attr[6]):
  268.             fdesc = typeinfo.GetFuncDesc(j)
  269.             self._AddFunc_(typeinfo,fdesc,bForUser)
  270.  
  271.         # Loop over all variables (ie, properties)
  272.         for j in xrange(attr[7]):
  273.             fdesc = typeinfo.GetVarDesc(j)
  274.             self._AddVar_(typeinfo,fdesc,bForUser)
  275.         
  276.         # Now post-process the maps.  For any "Get" or "Set" properties
  277.         # that have arguments, we must turn them into methods.  If a method
  278.         # of the same name already exists, change the name.
  279.         for key, item in self.propMapGet.items():
  280.             self._propMapGetCheck_(key,item)
  281.                     
  282.         for key, item in self.propMapPut.items():
  283.             self._propMapPutCheck_(key,item)
  284.  
  285.     def CountInOutOptArgs(self, argTuple):
  286.         "Return tuple counting in/outs/OPTS.  Sum of result may not be len(argTuple), as some args may be in/out."
  287.         ins = out = opts = 0
  288.         for argCheck in argTuple:
  289.             inOut = argCheck[1]
  290.             if inOut==0:
  291.                 ins = ins + 1
  292.                 out = out + 1
  293.             else:
  294.                 if inOut & pythoncom.PARAMFLAG_FIN:
  295.                     ins = ins + 1
  296.                 if inOut & pythoncom.PARAMFLAG_FOPT:
  297.                     opts = opts + 1
  298.                 if inOut & pythoncom.PARAMFLAG_FOUT:
  299.                     out = out + 1
  300.         return ins, out, opts
  301.  
  302.     def MakeFuncMethod(self, entry, name, bMakeClass = 1):
  303.         # If we have a type description, and not varargs...
  304.         if entry.desc is not None and (len(entry.desc) < 6 or entry.desc[6]!=-1):
  305.             return self.MakeDispatchFuncMethod(entry, name, bMakeClass)
  306.         else:
  307.             return self.MakeVarArgsFuncMethod(entry, name, bMakeClass)
  308.  
  309.     def MakeDispatchFuncMethod(self, entry, name, bMakeClass = 1):
  310.         fdesc = entry.desc
  311.         doc = entry.doc
  312.         names = entry.names
  313.         ret = []
  314.         if bMakeClass:
  315.             linePrefix = "\t"
  316.             defNamedOptArg = "defaultNamedOptArg"
  317.             defNamedNotOptArg = "defaultNamedNotOptArg"
  318.             defUnnamedArg = "defaultUnnamedArg"
  319.         else:
  320.             linePrefix = ""
  321.             defNamedOptArg = "pythoncom.Missing"
  322.             defNamedNotOptArg = "pythoncom.Missing"
  323.             defUnnamedArg = "pythoncom.Missing"
  324.         id = fdesc[0]
  325.         s = linePrefix + 'def ' + name + '(self' + BuildCallList(fdesc, names, defNamedOptArg, defNamedNotOptArg, defUnnamedArg) + '):'
  326.         ret.append(s)
  327.         if doc and doc[1]:
  328.             ret.append(linePrefix + '\t"""' + doc[1] + '"""')
  329.  
  330. #        print "fdesc is ", fdesc
  331.  
  332.         resclsid = entry.GetResultCLSID()
  333.         if resclsid:
  334.             resclsid = "'%s'" % resclsid
  335.         else:
  336.             resclsid = 'None'
  337.         # Strip the default values from the arg desc
  338.         retDesc = fdesc[8][:2]
  339.         argsDesc = tuple(map(lambda what: what[:2], fdesc[2]))
  340.         # The runtime translation of the return types is expensive, so when we know the
  341.         # return type of the function, there is no need to check the type at runtime.
  342.         # To qualify, this function must return a "simple" type, and have no byref args.
  343.         # Check if we have byrefs or anything in the args which mean we still need a translate.
  344.         param_flags = map(lambda what: what[1], fdesc[2])
  345.         bad_params = filter(lambda flag: flag & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FRETVAL)!=0, param_flags)
  346.         s = None
  347.         if len(bad_params)==0 and len(retDesc)==2 and retDesc[1]==0:
  348.             rd = retDesc[0]
  349.             if NoTranslateMap.has_key(rd):
  350.                 s = '%s\treturn self._oleobj_.InvokeTypes(0x%x, LCID, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, argsDesc, _BuildArgList(fdesc, names))
  351.             elif rd==pythoncom.VT_DISPATCH:
  352.                 s = '%s\tret = self._oleobj_.InvokeTypes(0x%x, LCID, %s, %s, %s%s)\n' % (linePrefix, id, fdesc[4], retDesc, `argsDesc`, _BuildArgList(fdesc, names))
  353.                 s = s + '%s\tif ret is not None: ret = win32com.client.Dispatch(ret, %s, %s, UnicodeToString=%d)\n' % (linePrefix,`name`, resclsid, NeedUnicodeConversions) 
  354.                 s = s + '%s\treturn ret' % (linePrefix)
  355.             elif rd == pythoncom.VT_BSTR:
  356.                 if NeedUnicodeConversions:
  357.                     s = "%s\t# Result is a Unicode object - perform automatic string conversion\n" % (linePrefix,)
  358.                     s = s + '%s\treturn str(self._oleobj_.InvokeTypes(0x%x, LCID, %s, %s, %s%s))' % (linePrefix, id, fdesc[4], retDesc, `argsDesc`, _BuildArgList(fdesc, names))
  359.                 else:
  360.                     s = "%s\t# Result is a Unicode object - return as-is for this version of Python\n" % (linePrefix,)
  361.                     s = s + '%s\treturn self._oleobj_.InvokeTypes(0x%x, LCID, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, `argsDesc`, _BuildArgList(fdesc, names))
  362.             # else s remains None
  363.         if s is None:
  364.             s = '%s\treturn self._ApplyTypes_(0x%x, %s, %s, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, argsDesc, `name`, resclsid, _BuildArgList(fdesc, names))
  365.  
  366.         ret.append(s)
  367.         ret.append("")
  368.         return ret
  369.  
  370.     def MakeVarArgsFuncMethod(self, entry, name, bMakeClass = 1):
  371.         fdesc = entry.desc
  372.         names = entry.names
  373.         doc = entry.doc
  374.         ret = []
  375.         argPrefix = "self"
  376.         if bMakeClass:
  377.             linePrefix = "\t"
  378.             defArg = "defaultArg"
  379.         else:
  380.             linePrefix = ""
  381.             defArg = "pythoncom.Missing"
  382.         ret.append(linePrefix + 'def ' + name + '(' + argPrefix + ', *args):')
  383.         if doc and doc[1]: ret.append(linePrefix + '\t"' + doc[1] + '"')
  384.         if fdesc:
  385.             invoketype = fdesc[4]
  386.         else:
  387.             invoketype = pythoncom.DISPATCH_METHOD
  388.         s = linePrefix + '\treturn self._get_good_object_(apply(self._oleobj_.Invoke,('
  389.         ret.append(s + hex(entry.dispid) + ",0,%d,1)+args),'%s')" % (invoketype, names[0]))
  390.         ret.append("")
  391.         return ret
  392.  
  393. # Note - "DispatchItem" poorly named - need a new intermediate class.
  394. class VTableItem(DispatchItem):
  395.     def Build(self, typeinfo, attr, bForUser = 1):
  396.         DispatchItem.Build(self, typeinfo, attr, bForUser)
  397.         assert typeinfo is not None, "Cant build vtables without type info!"
  398.  
  399.         def cmp_vtable_off(m1, m2):
  400.             return cmp(m1.desc[7], m2.desc[7])
  401.  
  402.         meth_list = self.mapFuncs.values()
  403.         meth_list.sort( cmp_vtable_off )
  404.         # Now turn this list into the run-time representation
  405.         # (ready for immediate use or writing to gencache)
  406.         self.vtableFuncs = []
  407.         for entry in meth_list:
  408.             self.vtableFuncs.append( (entry.names[0], entry.dispid, entry.desc[2], entry.desc[8], entry.names[1:]) )
  409.  
  410. # A Lazy dispatch item - builds an item on request using info from
  411. # an ITypeComp.  The dynamic module makes the called to build each item,
  412. # and also holds the references to the typeinfo and typecomp.
  413. class LazyDispatchItem(DispatchItem):
  414.     typename = "LazyDispatchItem"
  415.     def __init__(self, attr, doc):
  416.         self.clsid = attr[0]
  417.         DispatchItem.__init__(self, None, attr, doc, 0)
  418.  
  419. typeSubstMap = {
  420.     pythoncom.VT_INT: pythoncom.VT_I4,
  421.     pythoncom.VT_UINT: pythoncom.VT_I4,
  422.     pythoncom.VT_HRESULT: pythoncom.VT_I4,
  423. }
  424.  
  425. def _ResolveType(typerepr, itypeinfo):
  426.     # Resolve VT_USERDEFINED (often aliases or typed IDispatches)
  427. #    sys.stderr.write("- resolving %s - " % (typerepr,))
  428.  
  429.     if type(typerepr)==types.TupleType:
  430.         indir_vt, subrepr = typerepr
  431.         if indir_vt == pythoncom.VT_PTR:
  432.             # If it is a VT_PTR to a VT_USERDEFINED that is an IDispatch/IUnknown,
  433.             # then it resolves to simply the object.
  434.             # Otherwise, it becomes a ByRef of the resolved type
  435.             # We need to drop an indirection level on pointer to user defined interfaces.
  436.             # eg, (VT_PTR, (VT_USERDEFINED, somehandle)) needs to become VT_DISPATCH
  437.             # only when "somehandle" is an object.
  438.             # but (VT_PTR, (VT_USERDEFINED, otherhandle)) doesnt get the indirection dropped.
  439.             was_user = type(subrepr)==types.TupleType and subrepr[0]==pythoncom.VT_USERDEFINED
  440.             subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
  441.             if was_user and subrepr in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN, pythoncom.VT_RECORD]:
  442.                 # Drop the VT_PTR indirection
  443.                 return subrepr, sub_clsid, sub_doc
  444.             # Change PTR indirection to byref
  445.             return subrepr | pythoncom.VT_BYREF, sub_clsid, sub_doc
  446.         if indir_vt == pythoncom.VT_SAFEARRAY:
  447.             # resolve the array element, and convert to VT_ARRAY
  448.             subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
  449.             return pythoncom.VT_ARRAY | subrepr, sub_clsid, sub_doc
  450.  
  451.         if indir_vt == pythoncom.VT_USERDEFINED:
  452.             resultTypeInfo = itypeinfo.GetRefTypeInfo(subrepr)
  453.             resultAttr = resultTypeInfo.GetTypeAttr()
  454.             typeKind = resultAttr.typekind
  455.             if typeKind == pythoncom.TKIND_ALIAS:
  456.                 tdesc = resultAttr.tdescAlias
  457.                 return _ResolveType(tdesc, resultTypeInfo)
  458.             elif typeKind in [pythoncom.TKIND_ENUM, pythoncom.TKIND_MODULE]:
  459.                 # For now, assume Long
  460.                 return pythoncom.VT_I4, None, None
  461.  
  462.             elif typeKind in [pythoncom.TKIND_DISPATCH,
  463.                               pythoncom.TKIND_INTERFACE,
  464.                               pythoncom.TKIND_COCLASS]:
  465.                 # XXX - should probably get default interface for CO_CLASS???
  466.                 clsid = resultTypeInfo.GetTypeAttr()[0]
  467.                 retdoc = resultTypeInfo.GetDocumentation(-1)
  468.                 return pythoncom.VT_DISPATCH, clsid, retdoc
  469.  
  470.             elif typeKind == pythoncom.TKIND_RECORD:
  471.                 return pythoncom.VT_RECORD, None, None
  472.             raise NotSupportedException("Can not resolve alias or user-defined type")
  473.     return typeSubstMap.get(typerepr,typerepr), None, None
  474.  
  475. def _BuildArgList(fdesc, names):
  476.     "Builds list of args to the underlying Invoke method."
  477.     # Word has TypeInfo for Insert() method, but says "no args"
  478.     numArgs = max(fdesc[6], len(fdesc[2]))
  479.     names = list(names)
  480.     while None in names:
  481.         i = names.index(None)
  482.         names[i] = "arg%d" % (i,)
  483.     names = map(MakePublicAttributeName, names[1:])
  484.     while len(names) < numArgs:
  485.         names.append("arg%d" % (len(names),))
  486.     return "," + string.join(names, ", ")
  487.  
  488. valid_identifier_chars = string.letters + string.digits + "_"
  489.  
  490. # Given a "public name" (eg, the name of a class, function, etc)
  491. # make sure it is a legal (and reasonable!) Python name.
  492. def MakePublicAttributeName(className):
  493.     # Given a class attribute that needs to be public, but Python munges
  494.     # convert it.  Also need to be careful that the munging doesnt
  495.     # create duplicates - eg, just removing a leading "_" is likely to cause
  496.     # a clash.
  497.     if className[:2]=='__' and className[-2:]!='__':
  498.         return className[1:] + '_' # First '_' moved at the end.
  499.     elif iskeyword(className):
  500.         return string.capitalize(className)
  501.     # Strip non printable chars
  502.     return filter( lambda char: char in valid_identifier_chars, className)
  503.  
  504. # Given a default value passed by a type library, return a string with
  505. # an appropriate repr() for the type.
  506. # Takes a raw ELEMDESC and returns a repr string, or None
  507. # (NOTE: The string itself may be '"None"', which is valid, and different to None.
  508. # XXX - To do: Dates are probably screwed, but can they come in?
  509. def MakeDefaultArgRepr(defArgVal):
  510.   try:
  511.     inOut = defArgVal[1]
  512.   except IndexError:
  513.     # something strange - assume is in param.
  514.     inOut = pythoncom.PARAMFLAG_FIN
  515.  
  516.   if inOut & pythoncom.PARAMFLAG_FHASDEFAULT:
  517.     # hack for Unicode until it repr's better.
  518.     val = defArgVal[2]
  519.     if type(val) is UnicodeType:
  520.       return repr(str(val))
  521.     else:
  522.       return repr(val)
  523.   return None
  524.  
  525. def BuildCallList(fdesc, names, defNamedOptArg, defNamedNotOptArg, defUnnamedArg):
  526.   "Builds a Python declaration for a method."
  527.   # Names[0] is the func name - param names are from 1.
  528.   numArgs = len(fdesc[2])
  529.   numOptArgs = fdesc[6]
  530.   strval = ''
  531.   if numOptArgs==-1:    # Special value that says "var args after here"
  532.     numArgs = numArgs - 1
  533.   else:
  534.     firstOptArg = numArgs - numOptArgs
  535.   for arg in xrange(numArgs):
  536.     try:
  537.       argName = names[arg+1] 
  538.       namedArg = argName is not None
  539.     except IndexError:
  540.       namedArg = 0
  541.     if not namedArg: argName = "arg%d" % (arg)
  542.       
  543.     # See if the IDL specified a default value
  544.     defArgVal = MakeDefaultArgRepr(fdesc[2][arg])
  545.     if defArgVal is None:
  546.       # Unnamed arg - always allow default values.
  547.       if namedArg:
  548.         # Is a named argument
  549.         if arg >= firstOptArg:
  550.           defArgVal = defNamedOptArg
  551.         else:
  552.           defArgVal = defNamedNotOptArg
  553.       else:
  554.         defArgVal = defUnnamedArg
  555.  
  556.     argName = MakePublicAttributeName(argName)
  557.     strval = strval + ", " + argName
  558.     if defArgVal:
  559.       strval = strval + "=" + defArgVal
  560.   if numOptArgs==-1:
  561.     strval = strval + ", *" + names[-1]
  562.  
  563.   return strval
  564.  
  565.  
  566. if __name__=='__main__':
  567.   print "Use 'makepy.py' to generate Python code - this module is just a helper"
  568.