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