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_genpy.py < prev    next >
Encoding:
Python Source  |  2001-07-26  |  37.9 KB  |  926 lines

  1. """genpy.py - The worker for makepy.  See makepy.py for more details
  2.  
  3. This code was moved simply to speed Python in normal circumstances.  As the makepy.py
  4. is normally run from the command line, it reparses the code each time.  Now makepy
  5. is nothing more than the command line handler and public interface.
  6.  
  7. The makepy command line etc handling is also getting large enough in its own right!
  8. """
  9.  
  10. # NOTE - now supports a "demand" mechanism - the top-level is a package, and
  11. # each class etc can be made individually.
  12. # This should eventually become the default.
  13. # Then the old non-package technique should be removed.
  14. # There should be no b/w compat issues, and will just help clean the code.
  15. # This will be done once the new "demand" mechanism gets a good workout.
  16. import os
  17. import sys
  18. import string
  19. import time
  20. import win32com
  21.  
  22. import pythoncom
  23. import build
  24.  
  25. error = "makepy.error"
  26. makepy_version = "0.4.0" # Written to generated file.
  27.  
  28. GEN_FULL="full"
  29. GEN_DEMAND_BASE = "demand(base)"
  30. GEN_DEMAND_CHILD = "demand(child)"
  31.  
  32. # This map is used purely for the users benefit -it shows the
  33. # raw, underlying type of Alias/Enums, etc.  The COM implementation
  34. # does not use this map at runtime - all Alias/Enum have already
  35. # been translated.
  36. mapVTToTypeString = {
  37.     pythoncom.VT_I2: 'types.IntType',
  38.     pythoncom.VT_I4: 'types.IntType',
  39.     pythoncom.VT_R4: 'types.FloatType',
  40.     pythoncom.VT_R8: 'types.FloatType',
  41.     pythoncom.VT_BSTR: 'types.StringType',
  42.     pythoncom.VT_BOOL: 'types.IntType',
  43.     pythoncom.VT_VARIANT: 'types.TypeType',
  44.     pythoncom.VT_I1: 'types.IntType',
  45.     pythoncom.VT_UI1: 'types.IntType',
  46.     pythoncom.VT_UI2: 'types.IntType',
  47.     pythoncom.VT_UI4: 'types.IntType',
  48.     pythoncom.VT_I8: 'types.LongType',
  49.     pythoncom.VT_UI8: 'types.LongType',
  50.     pythoncom.VT_INT: 'types.IntType',
  51.     pythoncom.VT_DATE: 'pythoncom.PyTimeType',
  52.     pythoncom.VT_UINT: 'types.IntType',
  53. }
  54.  
  55. # Given a propget function's arg desc, return the default parameters for all
  56. # params bar the first.  Eg, then Python does a:
  57. # object.Property = "foo"
  58. # Python can only pass the "foo" value.  If the property has
  59. # multiple args, and the rest have default values, this allows
  60. # Python to correctly pass those defaults.
  61. def MakeDefaultArgsForPropertyPut(argsDesc):
  62.     ret = []
  63.     for desc in argsDesc[1:]:
  64.         default = build.MakeDefaultArgRepr(desc)
  65.         if default is None:
  66.             break
  67.         ret.append(default)
  68.     return tuple(ret)
  69.                             
  70.  
  71. def MakeMapLineEntry(dispid, wFlags, retType, argTypes, user, resultCLSID):
  72.     # Strip the default value
  73.     argTypes = tuple(map(lambda what: what[:2], argTypes))
  74.     return '(%s, %d, %s, %s, "%s", %s)' % \
  75.         (dispid, wFlags, retType[:2], argTypes, user, resultCLSID)
  76.  
  77. def MakeEventMethodName(eventName):
  78.     if eventName[:2]=="On":
  79.         return eventName
  80.     else:
  81.         return "On"+eventName
  82.  
  83. def WriteSinkEventMap(obj):
  84.     print '\t_dispid_to_func_ = {'
  85.     for name, entry in obj.propMapGet.items() + obj.propMapPut.items() + obj.mapFuncs.items():
  86.         fdesc = entry.desc
  87.         id = fdesc[0]
  88.         print '\t\t%9d : "%s",' % (entry.desc[0], MakeEventMethodName(entry.names[0]))
  89.     print '\t\t}'
  90.     
  91.  
  92. # MI is used to join my writable helpers, and the OLE
  93. # classes.
  94. class WritableItem:
  95.     def __cmp__(self, other):
  96.         "Compare for sorting"   
  97.         ret = cmp(self.order, other.order)
  98.         if ret==0 and self.doc: ret = cmp(self.doc[0], other.doc[0])
  99.         return ret
  100.     def __repr__(self):
  101.         return "OleItem: doc=%s, order=%d" % (`self.doc`, self.order)
  102.  
  103.  
  104. class RecordItem(build.OleItem, WritableItem):
  105.   order = 9
  106.   typename = "RECORD"
  107.  
  108.   def __init__(self, typeInfo, typeAttr, doc=None, bForUser=1):
  109. ##    sys.stderr.write("Record %s: size %s\n" % (doc,typeAttr.cbSizeInstance))
  110. ##    sys.stderr.write(" cVars = %s\n" % (typeAttr.cVars,))
  111. ##    for i in range(typeAttr.cVars):
  112. ##        vdesc = typeInfo.GetVarDesc(i)
  113. ##        sys.stderr.write(" Var %d has value %s, type %d, desc=%s\n" % (i, vdesc.value, vdesc.varkind, vdesc.elemdescVar))
  114. ##        sys.stderr.write(" Doc is %s\n" % (typeInfo.GetDocumentation(vdesc.memid),))
  115.  
  116.     build.OleItem.__init__(self, doc)
  117.     self.clsid = typeAttr[0]
  118.  
  119.   def WriteClass(self, generator):
  120.     pass
  121.  
  122. # Given an enum, write all aliases for it.
  123. # (no longer necessary for new style code, but still used for old code.
  124. def WriteAliasesForItem(item, aliasItems):
  125.   for alias in aliasItems.values():
  126.     if item.doc and alias.aliasDoc and (alias.aliasDoc[0]==item.doc[0]):
  127.       alias.WriteAliasItem(aliasItems)
  128.       
  129. class AliasItem(build.OleItem, WritableItem):
  130.   order = 2
  131.   typename = "ALIAS"
  132.  
  133.   def __init__(self, typeinfo, attr, doc=None, bForUser = 1):
  134.     build.OleItem.__init__(self, doc)
  135.  
  136.     ai = attr[14]
  137.     self.attr = attr
  138.     if type(ai) == type(()) and \
  139.       type(ai[1])==type(0): # XXX - This is a hack - why tuples?  Need to resolve?
  140.       href = ai[1]
  141.       alinfo = typeinfo.GetRefTypeInfo(href)
  142.       self.aliasDoc = alinfo.GetDocumentation(-1)
  143.       self.aliasAttr = alinfo.GetTypeAttr()
  144.     else:
  145.       self.aliasDoc = None
  146.       self.aliasAttr = None
  147.  
  148.   def WriteAliasItem(self, aliasDict):
  149.     # we could have been written as part of an alias dependency
  150.     if self.bWritten:
  151.       return
  152.  
  153.     if self.aliasDoc:
  154.       depName = self.aliasDoc[0]
  155.       if aliasDict.has_key(depName):
  156.         aliasDict[depName].WriteAliasItem(aliasDict)
  157.       print self.doc[0] + " = " + depName
  158.     else:
  159.       ai = self.attr[14]
  160.       if type(ai) == type(0):
  161.         try:
  162.           typeStr = mapVTToTypeString[ai]
  163.           print "# %s=%s" % (self.doc[0], typeStr)
  164.         except KeyError:
  165.           print self.doc[0] + " = None # Can't convert alias info " + str(ai)
  166.     print
  167.     self.bWritten = 1
  168.  
  169. class EnumerationItem(build.OleItem, WritableItem):
  170.   order = 1
  171.   typename = "ENUMERATION"
  172.  
  173.   def __init__(self, typeinfo, attr, doc=None, bForUser=1):
  174.     build.OleItem.__init__(self, doc)
  175.  
  176.     self.clsid = attr[0]
  177.     self.mapVars = {}
  178.     typeFlags = attr[11]
  179.     self.hidden = typeFlags & pythoncom.TYPEFLAG_FHIDDEN or \
  180.                   typeFlags & pythoncom.TYPEFLAG_FRESTRICTED
  181.  
  182.     for j in range(attr[7]):
  183.       vdesc = typeinfo.GetVarDesc(j)
  184.       name = typeinfo.GetNames(vdesc[0])[0]
  185.       self.mapVars[name] = build.MapEntry(vdesc)
  186.  
  187. ##  def WriteEnumerationHeaders(self, aliasItems):
  188. ##    enumName = self.doc[0]
  189. ##    print "%s=constants # Compatibility with previous versions." % (enumName)
  190. ##    WriteAliasesForItem(self, aliasItems)
  191.     
  192.   def WriteEnumerationItems(self):
  193.     enumName = self.doc[0]
  194.     # Write in name alpha order
  195.     names = self.mapVars.keys()
  196.     names.sort()
  197.     for name in names:
  198.       entry = self.mapVars[name]
  199.       vdesc = entry.desc
  200.       if vdesc[4] == pythoncom.VAR_CONST:
  201.         if type(vdesc[1])==type(0):
  202.           if vdesc[1]==0x80000000: # special case
  203.             use = "0x80000000"
  204.           else:
  205.             use = hex(vdesc[1])
  206.         else:
  207.           use = repr(str(vdesc[1]))
  208.         print "\t%-30s=%-10s # from enum %s" % (build.MakePublicAttributeName(name), use, enumName)
  209.  
  210. class VTableItem(build.VTableItem, WritableItem):
  211.     order = 4
  212.  
  213.     def WriteClass(self, generator):
  214.         self.WriteVTableMap(generator)
  215.         self.bWritten = 1
  216.  
  217.     def WriteVTableMap(self, generator):
  218.         print "%s_vtables_dispatch_ = %d" % (self.python_name, self.bIsDispatch)
  219.         print "%s_vtables_ = " % (self.python_name, ) ,
  220.         print repr(self.vtableFuncs)
  221.         print
  222.  
  223. class DispatchItem(build.DispatchItem, WritableItem):
  224.     order = 3
  225.  
  226.     def __init__(self, typeinfo, attr, doc=None):
  227.         build.DispatchItem.__init__(self, typeinfo, attr, doc)
  228.         self.type_attr = attr
  229.  
  230.     def WriteClass(self, generator):
  231.       wTypeFlags = self.type_attr.wTypeFlags
  232.       if not self.bIsDispatch and not self.type_attr.typekind == pythoncom.TKIND_DISPATCH:
  233.           return
  234.       # This is pretty screwey - now we have vtable support we
  235.       # should probably rethink this (ie, maybe write both sides for sinks, etc)
  236.       if self.bIsSink:
  237.           self.WriteEventSinkClassHeader(generator)
  238.           self.WriteCallbackClassBody(generator)
  239.       else:
  240.           self.WriteClassHeader(generator)
  241.           self.WriteClassBody(generator)
  242.       print
  243.       self.bWritten = 1
  244.  
  245.     def WriteClassHeader(self, generator):
  246.         generator.checkWriteDispatchBaseClass()
  247.         doc = self.doc
  248.         print 'class ' + self.python_name + '(DispatchBaseClass):'
  249.         if doc[1]: print '\t"""' + doc[1] + '"""'
  250.         try:
  251.             progId = pythoncom.ProgIDFromCLSID(self.clsid)
  252.             print "\t# This class is creatable by the name '%s'" % (progId)
  253.         except pythoncom.com_error:
  254.             pass
  255.         clsidStr = str(self.clsid)
  256.         print "\tCLSID = pythoncom.MakeIID('" + clsidStr + "')"
  257.         print
  258.         self.bWritten = 1
  259.  
  260.     def WriteEventSinkClassHeader(self, generator):
  261.         generator.checkWriteEventBaseClass()
  262.         doc = self.doc
  263.         print 'class ' + self.python_name + ':'
  264.         if doc[1]: print '\t\"' + doc[1] + '\"'
  265.         try:
  266.             progId = pythoncom.ProgIDFromCLSID(self.clsid)
  267.             print "\t# This class is creatable by the name '%s'" % (progId)
  268.         except pythoncom.com_error:
  269.             pass
  270.         clsidStr = str(self.clsid)
  271.         print '\tCLSID = CLSID_Sink = pythoncom.MakeIID(\'' + clsidStr + '\')'
  272.         print '\t_public_methods_ = [] # For COM Server support'
  273.         WriteSinkEventMap(self)
  274.         print
  275.         print '\tdef __init__(self, oobj = None):'
  276.         print "\t\tif oobj is None:"
  277.         print "\t\t\tself._olecp = None"
  278.         print "\t\telse:"
  279.         print '\t\t\timport win32com.server.util'
  280.         print '\t\t\tfrom win32com.server.policy import EventHandlerPolicy'
  281.         print '\t\t\tcpc=oobj._oleobj_.QueryInterface(pythoncom.IID_IConnectionPointContainer)'
  282.         print '\t\t\tcp=cpc.FindConnectionPoint(self.CLSID_Sink)'
  283.         print '\t\t\tcookie=cp.Advise(win32com.server.util.wrap(self, usePolicy=EventHandlerPolicy))'
  284.         print '\t\t\tself._olecp,self._olecp_cookie = cp,cookie'
  285.         print '\tdef __del__(self):'
  286.         print '\t\ttry:'
  287.         print '\t\t\tself.close()'
  288.         print '\t\texcept pythoncom.com_error:'
  289.         print '\t\t\tpass'
  290.         print '\tdef close(self):'
  291.         print '\t\tif self._olecp is not None:'
  292.         print '\t\t\tcp,cookie,self._olecp,self._olecp_cookie = self._olecp,self._olecp_cookie,None,None'
  293.         print '\t\t\tcp.Unadvise(cookie)'
  294.         print '\tdef _query_interface_(self, iid):'
  295.         print '\t\timport win32com.server.util'
  296.         print '\t\tif iid==self.CLSID_Sink: return win32com.server.util.wrap(self)'
  297.         print
  298.         self.bWritten = 1
  299.  
  300.     def WriteCallbackClassBody(self, generator):
  301.         print "\t# Handlers for the control"
  302.         print "\t# If you create handlers, they should have the following prototypes:"
  303.         for name, entry in self.propMapGet.items() + self.propMapPut.items() + self.mapFuncs.items():
  304.             fdesc = entry.desc
  305.             id = fdesc[0]
  306.             methName = MakeEventMethodName(entry.names[0])
  307.             print '#\tdef ' + methName + '(self' + build.BuildCallList(fdesc, entry.names, "defaultNamedOptArg", "defaultNamedNotOptArg","defaultUnnamedArg") + '):'
  308.             if entry.doc and entry.doc[1]: print '#\t\t"' + entry.doc[1] + '"'
  309.         print
  310.         self.bWritten = 1
  311.  
  312.     def WriteClassBody(self, generator):
  313.         doc = self.doc
  314.         # Write in alpha order.
  315.         names = self.mapFuncs.keys()
  316.         names.sort()
  317.         specialItems = {"count":None, "item":None,"value":None,"_newenum":None} # If found, will end up with (entry, invoke_tupe)
  318.         itemCount = None
  319.         for name in names:
  320.             entry=self.mapFuncs[name]
  321.             # If not accessible via IDispatch, then we can't use it here.
  322.             if entry.desc[3] != pythoncom.FUNC_DISPATCH:
  323.                 continue
  324.             if entry.desc[0]==pythoncom.DISPID_VALUE:
  325.                 lkey = "value"
  326.             elif entry.desc[0]==pythoncom.DISPID_NEWENUM:
  327.                 specialItems["_newenum"] = (entry, entry.desc[4], None)
  328.                 continue # Dont build this one now!
  329.             else:
  330.                 lkey = string.lower(name)
  331.             if specialItems.has_key(lkey) and specialItems[lkey] is None: # remember if a special one.
  332.                 specialItems[lkey] = (entry, entry.desc[4], None)
  333.             if generator.bBuildHidden or not entry.hidden:
  334.                 if entry.GetResultName():
  335.                     print '\t# Result is of type ' + entry.GetResultName()
  336.                 if entry.wasProperty:
  337.                     print '\t# The method %s is actually a property, but must be used as a method to correctly pass the arguments' % name
  338.                 ret = self.MakeFuncMethod(entry,build.MakePublicAttributeName(name))
  339.                 for line in ret:
  340.                     print line
  341.         print "\t_prop_map_get_ = {"
  342.         names = self.propMap.keys(); names.sort()
  343.         for key in names:
  344.             entry = self.propMap[key]
  345.             if generator.bBuildHidden or not entry.hidden:
  346.                 resultName = entry.GetResultName()
  347.                 if resultName:
  348.                     print "\t\t# Property '%s' is an object of type '%s'" % (key, resultName)
  349.                 lkey = string.lower(key)
  350.                 details = entry.desc
  351.                 resultDesc = details[2]
  352.                 argDesc = ()
  353.                 mapEntry = MakeMapLineEntry(details[0], pythoncom.DISPATCH_PROPERTYGET, resultDesc, argDesc, key, entry.GetResultCLSIDStr())
  354.             
  355.                 if entry.desc[0]==pythoncom.DISPID_VALUE:
  356.                     lkey = "value"
  357.                 elif entry.desc[0]==pythoncom.DISPID_NEWENUM:
  358.                     # XXX - should DISPATCH_METHOD in the next line use the invtype?
  359.                     specialItems["_newenum"] = (entry, pythoncom.DISPATCH_METHOD, mapEntry)
  360.                     continue # Dont build this one now!
  361.                 else:
  362.                     lkey = string.lower(key)
  363.                 if specialItems.has_key(lkey) and specialItems[lkey] is None: # remember if a special one.
  364.                     specialItems[lkey] = (entry, pythoncom.DISPATCH_PROPERTYGET, mapEntry)
  365.  
  366.                 print '\t\t"%s": %s,' % (key, mapEntry)
  367.         names = self.propMapGet.keys(); names.sort()
  368.         for key in names:
  369.             entry = self.propMapGet[key]
  370.             if generator.bBuildHidden or not entry.hidden:
  371.                 if entry.GetResultName():
  372.                     print "\t\t# Method '%s' returns object of type '%s'" % (key, entry.GetResultName())
  373.                 details = entry.desc
  374.                 lkey = string.lower(key)
  375.                 argDesc = details[2]
  376.                 resultDesc = details[8]
  377.                 mapEntry = MakeMapLineEntry(details[0], pythoncom.DISPATCH_PROPERTYGET, resultDesc, argDesc, key, entry.GetResultCLSIDStr())
  378.                 if entry.desc[0]==pythoncom.DISPID_VALUE:
  379.                     lkey = "value"
  380.                 elif entry.desc[0]==pythoncom.DISPID_NEWENUM:
  381.                     specialItems["_newenum"] = (entry, pythoncom.DISPATCH_METHOD, mapEntry)
  382.                     continue # Dont build this one now!
  383.                 else:
  384.                     lkey = string.lower(key)
  385.                 if specialItems.has_key(lkey) and specialItems[lkey] is None: # remember if a special one.
  386.                     specialItems[lkey]=(entry, pythoncom.DISPATCH_PROPERTYGET, mapEntry)
  387.                 print '\t\t"%s": %s,' % (key, mapEntry)
  388.  
  389.         print "\t}"
  390.  
  391.         print "\t_prop_map_put_ = {"
  392.         # These are "Invoke" args
  393.         names = self.propMap.keys(); names.sort()
  394.         for key in names:
  395.             entry = self.propMap[key]
  396.             if generator.bBuildHidden or not entry.hidden:
  397.                 lkey=string.lower(key)
  398.                 details = entry.desc
  399.                 # If default arg is None, write an empty tuple
  400.                 defArgDesc = build.MakeDefaultArgRepr(details[2])
  401.                 if defArgDesc is None:
  402.                     defArgDesc = ""
  403.                 else:
  404.                     defArgDesc = defArgDesc + ","
  405.                 print '\t\t"%s" : ((%s, LCID, %d, 0),(%s)),' % (key, details[0], pythoncom.DISPATCH_PROPERTYPUT, defArgDesc)
  406.  
  407.         names = self.propMapPut.keys(); names.sort()
  408.         for key in names:
  409.             entry = self.propMapPut[key]
  410.             if generator.bBuildHidden or not entry.hidden:
  411.                 details = entry.desc
  412.                 defArgDesc = MakeDefaultArgsForPropertyPut(details[2])
  413.                 print '\t\t"%s": ((%s, LCID, %d, 0),%s),' % (key, details[0], details[4], defArgDesc)
  414.         print "\t}"
  415.         
  416.         if specialItems["value"]:
  417.             entry, invoketype, propArgs = specialItems["value"]
  418.             if propArgs is None:
  419.                 typename = "method"
  420.                 ret = self.MakeFuncMethod(entry,'__call__')
  421.             else:
  422.                 typename = "property"
  423.                 ret = [ "\tdef __call__(self):\n\t\treturn apply(self._ApplyTypes_, %s )" % propArgs]
  424.             print "\t# Default %s for this class is '%s'" % (typename, entry.names[0])
  425.             for line in ret:
  426.                 print line
  427.             print "\t# str(ob) and int(ob) will use __call__"
  428.             print "\tdef __str__(self, *args):"
  429.             print "\t\ttry:"
  430.             print "\t\t\treturn str(apply( self.__call__, args))"
  431.             print "\t\texcept pythoncom.com_error:"
  432.             print "\t\t\treturn repr(self)"
  433.             print "\tdef __int__(self, *args):"
  434.             print "\t\treturn int(apply( self.__call__, args))"
  435.             
  436.  
  437.         if specialItems["_newenum"]:
  438.             enumEntry, invoketype, propArgs = specialItems["_newenum"]
  439.             resultCLSID = enumEntry.GetResultCLSIDStr()
  440.             # If we dont have a good CLSID for the enum result, assume it is the same as the Item() method.
  441.             if resultCLSID == "None" and self.mapFuncs.has_key("Item"):
  442.                 resultCLSID = self.mapFuncs["Item"].GetResultCLSIDStr()
  443.             print '\tdef _NewEnum(self):'
  444.             print '\t\t"Create an enumerator from this object"'
  445.             print '\t\treturn win32com.client.util.WrapEnum(self._oleobj_.InvokeTypes(%d,LCID,%d,(13, 10),()),%s)' % (pythoncom.DISPID_NEWENUM, enumEntry.desc[4], resultCLSID)
  446.             print '\tdef __getitem__(self, index):'
  447.             print '\t\t"Allow this class to be accessed as a collection"'
  448.             print "\t\tif not self.__dict__.has_key('_enum_'):"
  449.             print "\t\t\timport win32com.client.util"
  450.             print "\t\t\tself.__dict__['_enum_'] = self._NewEnum()"
  451.             print "\t\treturn self._enum_.__getitem__(index)"
  452.         else: # Not an Enumerator, but may be an "Item/Count" based collection
  453.             if specialItems["item"]:
  454.                 entry, invoketype, propArgs = specialItems["item"]
  455.                 print '\t#This class has Item property/method which may take args - allow indexed access'
  456.                 print '\tdef __getitem__(self, item):'
  457.                 print '\t\treturn self._get_good_object_(apply(self._oleobj_.Invoke, (0x%x, LCID, %d, 1, item)), "Item")' % (entry.desc[0], invoketype)
  458.         if specialItems["count"]:
  459.             entry, invoketype, propArgs = specialItems["count"]
  460.             if propArgs is None:
  461.                 typename = "method"
  462.                 ret = self.MakeFuncMethod(entry,'__len__')
  463.             else:
  464.                 typename = "property"
  465.                 ret = [ "\tdef __len__(self):\n\t\treturn apply(self._ApplyTypes_, %s )" % propArgs]
  466.             print "\t#This class has Count() %s - allow len(ob) to provide this" % (typename)
  467.             for line in ret:
  468.                 print line
  469.             # Also include a __nonzero__
  470.             print "\t#This class has a __len__ - this is needed so 'if object:' always returns TRUE."
  471.             print "\tdef __nonzero__(self):"
  472.             print "\t\treturn 1"
  473.  
  474. class CoClassItem(build.OleItem, WritableItem):
  475.   order = 5
  476.   typename = "COCLASS"
  477.  
  478.   def __init__(self, typeinfo, attr, doc=None, sources = [], interfaces = [], bForUser=1):
  479.     build.OleItem.__init__(self, doc)
  480.     self.clsid = attr[0]
  481.     self.sources = sources
  482.     self.interfaces = interfaces
  483.     self.bIsDispatch = 1 # Pretend it is so it is written to the class map.
  484.  
  485.   def WriteClass(self, generator):
  486.     generator.checkWriteCoClassBaseClass()
  487.     doc = self.doc
  488.     if generator.generate_type == GEN_DEMAND_CHILD:
  489.       # Some special imports we must setup.
  490.       referenced_items = []
  491.       for ref, flag in self.sources:
  492.         referenced_items.append(ref)
  493.       for ref, flag in self.interfaces:
  494.         referenced_items.append(ref)
  495.       print "import sys"
  496.       for ref in referenced_items:
  497.         print "__import__('%s.%s')" % (generator.base_mod_name, ref.python_name)
  498.         print "%s = sys.modules['%s.%s'].%s" % (ref.python_name, generator.base_mod_name, ref.python_name, ref.python_name)
  499.     try:
  500.       progId = pythoncom.ProgIDFromCLSID(self.clsid)
  501.       print "# This CoClass is known by the name '%s'" % (progId)
  502.     except pythoncom.com_error:
  503.       pass
  504.     print 'class %s(CoClassBaseClass): # A CoClass' % (self.python_name)
  505.     if doc and doc[1]: print '\t# ' + doc[1]
  506.     clsidStr = str(self.clsid)
  507.     print '\tCLSID = pythoncom.MakeIID("%s")' % (clsidStr)
  508.     print '\tcoclass_sources = ['
  509.     defItem = None
  510.     for item, flag in self.sources:
  511.       if flag & pythoncom.IMPLTYPEFLAG_FDEFAULT:
  512.         defItem = item
  513.       # check if non-dispatchable - if so no real Python class has been written.  Write the iid as a string instead.
  514.       if item.bIsDispatch: key = item.python_name
  515.       else: key = repr(str(item.clsid)) # really the iid.
  516.       print '\t\t%s,' % (key)
  517.     print '\t]'
  518.     if defItem:
  519.       if defItem.bIsDispatch: defName = defItem.python_name
  520.       else: defName = repr(str(defItem.clsid)) # really the iid.
  521.       print '\tdefault_source = %s' % (defName,)
  522.     print '\tcoclass_interfaces = ['
  523.     defItem = None
  524.     for item, flag in self.interfaces:
  525.       if flag & pythoncom.IMPLTYPEFLAG_FDEFAULT: # and dual:
  526.         defItem = item
  527.       # check if non-dispatchable - if so no real Python class has been written.  Write the iid as a string instead.
  528.       if item.bIsDispatch: key = item.python_name
  529.       else: key = repr(str(item.clsid)) # really the iid.
  530.       print '\t\t%s,' % (key,)
  531.     print '\t]'
  532.     if defItem:
  533.       if defItem.bIsDispatch: defName = defItem.python_name
  534.       else: defName = repr(str(defItem.clsid)) # really the iid.
  535.       print '\tdefault_interface = %s' % (defName,)
  536.     self.bWritten = 1
  537.     print
  538.  
  539. class GeneratorProgress:
  540.     def __init__(self):
  541.         pass
  542.     def Starting(self, tlb_desc):
  543.         """Called when the process starts.
  544.         """
  545.         self.tlb_desc = tlb_desc
  546.     def Finished(self):
  547.         """Called when the process is complete.
  548.         """
  549.     def SetDescription(self, desc, maxticks = None):
  550.         """We are entering a major step.  If maxticks, then this
  551.         is how many ticks we expect to make until finished
  552.         """
  553.     def Tick(self, desc = None):
  554.         """Minor progress step.  Can provide new description if necessary
  555.         """
  556.     def VerboseProgress(self, desc):
  557.         """Verbose/Debugging output.
  558.         """
  559.     def LogWarning(self, desc):
  560.         """If a warning is generated
  561.         """
  562.     def LogBeginGenerate(self, filename):
  563.         pass
  564.     def Close(self):
  565.         pass
  566.  
  567. class Generator:
  568.   def __init__(self, typelib, sourceFilename, progressObject, bBuildHidden=1, bUnicodeToString=0):
  569.     self.bHaveWrittenDispatchBaseClass = 0
  570.     self.bHaveWrittenCoClassBaseClass = 0
  571.     self.bHaveWrittenEventBaseClass = 0
  572.  
  573.     self.typelib = typelib
  574.     self.sourceFilename = sourceFilename
  575.     self.bBuildHidden = bBuildHidden
  576.     self.bUnicodeToString = bUnicodeToString
  577.     self.progress = progressObject
  578.     # These 2 are later additions and most of the code still 'print's...
  579.     self.file = None
  580.  
  581.   def BuildOleItemsFromType(self, look_name = None):
  582.     assert self.bBuildHidden, "This code doesnt look at the hidden flag - I thought everyone set it true!?!?!"
  583.     oleItems = {}
  584.     enumItems = {}
  585.     recordItems = {}
  586.     vtableItems = {}
  587.     for i in xrange(self.typelib.GetTypeInfoCount()):
  588.       info = self.typelib.GetTypeInfo(i)
  589.       infotype = self.typelib.GetTypeInfoType(i)
  590.       doc = self.typelib.GetDocumentation(i)
  591.       attr = info.GetTypeAttr()
  592.       itemClass = None
  593.       if infotype == pythoncom.TKIND_ENUM or infotype == pythoncom.TKIND_MODULE:
  594.         if look_name is not None: continue
  595.         newItem = EnumerationItem(info, attr, doc)
  596.         enumItems[newItem.doc[0]] = newItem
  597.       # We never hide interfaces (MSAccess, for example, nominates interfaces as
  598.       # hidden, assuming that you only ever use them via the CoClass)
  599.       elif infotype in [pythoncom.TKIND_DISPATCH, pythoncom.TKIND_INTERFACE]:
  600.         if look_name is not None and doc[0]!=look_name:
  601.           continue
  602.         if infotype == pythoncom.TKIND_DISPATCH:
  603.             if not oleItems.has_key(attr[0]):
  604.                 newItem = DispatchItem(info, attr, doc)
  605.                 oleItems[newItem.clsid] = newItem
  606.                 # If this DISPATCH interface is not dual, then we are done.
  607.             if not (attr.wTypeFlags & pythoncom.TYPEFLAG_FDUAL):
  608. #                sys.stderr.write("interface " + doc[0] + " is not dual\n");
  609.                 continue
  610.             # If a dual dispatch interface, get the _real_ interface
  611.             refhtype = info.GetRefTypeOfImplType(-1)
  612.             info = info.GetRefTypeInfo(refhtype)
  613.             attr = info.GetTypeAttr()
  614. #        assert infotype == pythoncom.TKIND_INTERFACE, "Must be a real interface at this point"
  615.         if vtableItems.has_key(attr[0]):
  616.             continue # already built by CoClass processing.
  617. #        sys.stderr.write("Have interface " + doc[0] + "\n");
  618.         newItem = VTableItem(info, attr, doc)
  619.         vtableItems[newItem.clsid] = newItem
  620.  
  621.       elif infotype == pythoncom.TKIND_RECORD or infotype == pythoncom.TKIND_UNION:
  622.         if look_name is not None: continue
  623.         newItem = RecordItem(info, attr, doc)
  624.         recordItems[newItem.clsid] = newItem
  625.       elif infotype == pythoncom.TKIND_ALIAS:
  626.         # We dont care about alias' - handled intrinsicly.
  627.         continue
  628.       elif infotype == pythoncom.TKIND_COCLASS:
  629.         # try to find the source and dispinterfaces for the coclass
  630.         # We no longer generate specific OCX support for the CoClass, as there
  631.         # may be multiple Dispatch and multiple source interfaces.  We cant
  632.         # predict much in this scenario, so we move the responsibility to
  633.         # the Python programmer.
  634.         # (It also keeps win32ui(ole) out of the core generated import dependencies.
  635.         if look_name is not None and look_name != doc[0]: continue
  636.         sources = []
  637.         interfaces = []
  638.         for j in range(attr[8]):
  639.           flags = info.GetImplTypeFlags(j)
  640.           refType = info.GetRefTypeInfo(info.GetRefTypeOfImplType(j))
  641.           refAttr = refType.GetTypeAttr()
  642.           isSource = flags & pythoncom.IMPLTYPEFLAG_FSOURCE
  643.           name = build.MakePublicAttributeName(refType.GetDocumentation(-1)[0])
  644. #          sys.stderr.write("Attr typeflags for coclass referenced object %s=%d (%d), typekind=%d\n" % (name, refAttr.wTypeFlags, refAttr.wTypeFlags & pythoncom.TYPEFLAG_FDUAL,refAttr.typekind))
  645.           if refAttr.typekind == pythoncom.TKIND_DISPATCH:
  646.               if oleItems.has_key(refAttr[0]):
  647.                 dispItem = oleItems[refAttr[0]]
  648.               else:
  649.                 dispItem = DispatchItem(refType, refAttr, refType.GetDocumentation(-1))
  650.                 oleItems[dispItem.clsid] = dispItem
  651.               if flags & pythoncom.IMPLTYPEFLAG_FSOURCE:
  652.                 dispItem.bIsSink = 1
  653.                 sources.append((dispItem, flags))
  654.               else:
  655.                 interfaces.append((dispItem, flags))
  656.               # If dual interface, make do that too.
  657.               if not refAttr[11] & pythoncom.TYPEFLAG_FDUAL:
  658.                 continue
  659.               refType = refType.GetRefTypeInfo(refType.GetRefTypeOfImplType(-1))
  660.               refAttr = refType.GetTypeAttr()
  661.           assert refAttr.typekind == pythoncom.TKIND_INTERFACE, "must be interface bynow!"
  662.           if refAttr.typekind == pythoncom.TKIND_DISPATCH:
  663.               if vtableItems.has_key(refAttr[0]):
  664.                 dispItem = vtableItems[refAttr[0]]
  665.               else:
  666.                 dispItem = VTableItem(refType, refAttr, refType.GetDocumentation(-1))
  667.                 vtableItems[dispItem.clsid] = dispItem
  668.  
  669.               if flags & pythoncom.IMPLTYPEFLAG_FSOURCE:
  670.                   dispItem.bIsSink = 1
  671.                   sources.append((dispItem, flags))
  672.               else:
  673.                   interfaces.append((dispItem, flags))
  674.  
  675.         # Done generating children - now the CoClass itself.
  676.         newItem = CoClassItem(info, attr, doc, sources, interfaces)
  677.         oleItems[newItem.clsid] = newItem
  678.       else:
  679.         self.progress.LogWarning("Unknown TKIND found: %d" % infotype)
  680.   
  681.     return oleItems, enumItems, recordItems, vtableItems
  682.  
  683.   def generate(self, file, is_for_demand = 0):
  684.     if is_for_demand:
  685.       self.generate_type = GEN_DEMAND_BASE
  686.     else:
  687.       self.generate_type = GEN_FULL
  688.     self.file = file
  689.     oldOut = sys.stdout
  690.     sys.stdout = file
  691.     try:
  692.       self.do_generate()
  693.     finally:
  694.       sys.stdout = oldOut
  695.       self.file = None
  696.       self.progress.Finished()
  697.  
  698.   def do_gen_file_header(self):
  699.     la = self.typelib.GetLibAttr()
  700.     moduleDoc = self.typelib.GetDocumentation(-1)
  701.     docDesc = ""
  702.     if moduleDoc[1]:
  703.       docDesc = moduleDoc[1]
  704.  
  705.     print '# Created by makepy.py version %s' % (makepy_version,)
  706.     print '# By python version %s' % (sys.version,)
  707.     if self.sourceFilename:
  708.         print "# From type library '%s'" % (os.path.split(self.sourceFilename)[1],)
  709.     print '# On %s' % time.ctime(time.time())
  710. #    print '#\n# Command line used:', string.join(sys.argv[1:]," ")
  711.  
  712.  
  713.     print '"""' + docDesc + '"""'
  714.  
  715.     print 'makepy_version =', `makepy_version`
  716.     try:
  717.         print 'python_version = 0x%x' % (sys.hexversion,)
  718.     except AttributeError:
  719.         print 'python_version = 0x0 # Presumably Python 1.5.2 - 0x0 is not a problem'
  720.     print
  721.     print 'import win32com.client.CLSIDToClass, pythoncom'
  722.     print
  723.     print '# The following 3 lines may need tweaking for the particular server'
  724.     print '# Candidates are pythoncom.Missing and pythoncom.Empty'
  725.     print 'defaultNamedOptArg=pythoncom.Missing'
  726.     print 'defaultNamedNotOptArg=pythoncom.Missing'
  727.     print 'defaultUnnamedArg=pythoncom.Missing'
  728.     print
  729.     print 'CLSID = pythoncom.MakeIID(\'' + str(la[0]) + '\')'
  730.     print 'MajorVersion = ' + str(la[3])
  731.     print 'MinorVersion = ' + str(la[4])
  732.     print 'LibraryFlags = ' + str(la[5])
  733.     print 'LCID = ' + hex(la[1])
  734.     print
  735.  
  736.   def do_generate(self):
  737.     moduleDoc = self.typelib.GetDocumentation(-1)
  738.     docDesc = ""
  739.     if moduleDoc[1]:
  740.       docDesc = moduleDoc[1]
  741.     self.progress.Starting(docDesc)
  742.     self.progress.SetDescription("Building definitions from type library...")
  743.  
  744.     self.do_gen_file_header()
  745.  
  746.     oleItems, enumItems, recordItems, vtableItems = self.BuildOleItemsFromType()
  747.  
  748.     self.progress.SetDescription("Generating...", len(oleItems)+len(enumItems)+len(vtableItems))
  749.  
  750.     # Generate the constants and their support.
  751.     if enumItems:
  752.         print "class constants:"
  753.         list = enumItems.values()
  754.         list.sort()
  755.         for oleitem in list:
  756.             oleitem.WriteEnumerationItems()
  757.             self.progress.Tick()
  758.         print
  759.  
  760.     if self.generate_type == GEN_FULL:
  761.       list = oleItems.values()
  762.       list.sort()
  763.       for oleitem in list:
  764.         self.progress.Tick()
  765.         oleitem.WriteClass(self)
  766.  
  767.       list = vtableItems.values()
  768.       list.sort()
  769.       for oleitem in list:
  770.         self.progress.Tick()
  771.         oleitem.WriteClass(self)
  772.     else:
  773.         self.progress.Tick(len(oleItems)+len(vtableItems))
  774.  
  775.     print 'RecordMap = {'
  776.     list = recordItems.values()
  777.     for record in list:
  778.         if str(record.clsid) == pythoncom.IID_NULL:
  779.             print "\t###%s: %s, # Typedef disabled because it doesn't have a non-null GUID" % (`record.doc[0]`, `str(record.clsid)`)
  780.         else:
  781.             print "\t%s: %s," % (`record.doc[0]`, `str(record.clsid)`)
  782.     print "}"
  783.     print
  784.  
  785.     # Write out _all_ my generated CLSID's in the map
  786.     if self.generate_type == GEN_FULL:
  787.       print 'CLSIDToClassMap = {'
  788.       for item in oleItems.values():
  789.           if item.bWritten and item.bIsDispatch:
  790.               print "\t'%s' : %s," % (str(item.clsid), item.python_name)
  791.       print '}'
  792.       print 'CLSIDToPackageMap = {}'
  793.       print 'win32com.client.CLSIDToClass.RegisterCLSIDsFromDict( CLSIDToClassMap )'
  794.       print "VTablesToPackageMap = {}"
  795.       print "VTablesToClassMap = {"
  796.       for item in vtableItems.values():
  797.           if not item.bIsDispatch:
  798.             print "\t'%s' : '%s'," % (item.clsid,item.python_name)
  799.       print '}'
  800.       print 
  801.  
  802.     else:
  803.       print 'CLSIDToClassMap = {}'
  804.       print 'CLSIDToPackageMap = {'
  805.       for item in oleItems.values():
  806.         print "\t'%s' : %s," % (str(item.clsid), `item.python_name`)
  807.       print '}'
  808.       print "VTablesToClassMap = {}"
  809.       print "VTablesToPackageMap = {"
  810.       for item in vtableItems.values():
  811.           if not item.bIsDispatch:
  812.             print "\t'%s' : '%s'," % (item.clsid,item.python_name)
  813.       print '}'
  814.       print 
  815.  
  816.     print
  817.     print "VTablesNamesToCLSIDMap = {"
  818.     for item in vtableItems.values():
  819.         print "\t'%s' : '%s'," % (item.python_name, item.clsid)
  820.     print '}'
  821.     print
  822.  
  823.     if enumItems:
  824.       print 'win32com.client.constants.__dicts__.append(constants.__dict__)'
  825.     print
  826.  
  827.   def generate_child(self, child, dir):
  828.     "Generate a single child.  May force a few children to be built as we generate deps"
  829.     self.generate_type = GEN_DEMAND_CHILD
  830.     oldOut = sys.stdout
  831.  
  832.     la = self.typelib.GetLibAttr()
  833.     lcid = la[1]
  834.     clsid = la[0]
  835.     major=la[3]
  836.     minor=la[4]
  837.     self.base_mod_name = "win32com.gen_py." + str(clsid)[1:-1] + "x%sx%sx%s" % (lcid, major, minor)
  838.     try:
  839.       oleItems, enumItems, recordItems, vtableItems = self.BuildOleItemsFromType(child)
  840.       assert len(enumItems)==0 and len(recordItems)==0, "Not expecting anything other than dispatch/interface items"
  841.       assert len(oleItems)>0 or len(vtableItems)>0, "Could not find the name '%s'" % (child,)
  842.       # Make a map of iid: dispitem, vtableitem)
  843.       items = {}
  844.       for key, value in oleItems.items():
  845.           items[key] = (value,None)
  846.       for key, value in vtableItems.items():
  847.           existing = items.get(key, None)
  848.           if existing is not None:
  849.               new_val = existing[0], value
  850.           else:
  851.               new_val = None, value
  852.           items[key] = new_val
  853.  
  854.       self.progress.SetDescription("Generating...", len(items))
  855.       for oleitem, vtableitem in items.values():
  856.         an_item = oleitem or vtableitem
  857.         self.file = open(os.path.join(dir, an_item.python_name) + ".py", "w")
  858.         sys.stdout = self.file
  859.         try:
  860.           if oleitem is not None:
  861.             self.do_gen_child_item(oleitem)
  862.           if vtableitem is not None:
  863.             self.do_gen_child_item(vtableitem)
  864.           self.progress.Tick()
  865.         finally:
  866.           sys.stdout = oldOut
  867.           self.file.close()
  868.           self.file = None
  869.     finally:
  870.       sys.stdout = oldOut
  871.       self.progress.Finished()
  872.  
  873.   def do_gen_child_item(self, oleitem):
  874.     moduleDoc = self.typelib.GetDocumentation(-1)
  875.     docDesc = ""
  876.     if moduleDoc[1]:
  877.       docDesc = moduleDoc[1]
  878.     self.progress.Starting(docDesc)
  879.     self.progress.SetDescription("Building definitions from type library...")
  880.     self.do_gen_file_header()
  881.     oleitem.WriteClass(self)
  882.     if oleitem.bIsDispatch:
  883.         print 'win32com.client.CLSIDToClass.RegisterCLSID( "%s", %s )' % (oleitem.clsid, oleitem.python_name)
  884.  
  885.   def checkWriteDispatchBaseClass(self):
  886.     if not self.bHaveWrittenDispatchBaseClass:
  887.       print "from win32com.client import DispatchBaseClass"
  888.       self.bHaveWrittenDispatchBaseClass = 1
  889.  
  890.   def checkWriteCoClassBaseClass(self):
  891.     if not self.bHaveWrittenCoClassBaseClass:
  892.       print "class CoClassBaseClass:"
  893.       print '\tdef __init__(self, oobj=None):'
  894.       print '\t\tif oobj is None: oobj = pythoncom.new(self.CLSID)'
  895. #      print '\t\tself.__dict__["_oleobj_"] = oobj'
  896.       print '\t\tself.__dict__["_dispobj_"] = self.default_interface(oobj)'
  897.       # Provide a prettier name than the CLSID
  898.       print '\tdef __repr__(self):'
  899.       print '\t\treturn "<win32com.gen_py.%s.%s>" % (__doc__, self.__class__.__name__)'
  900.       print
  901.       print '\tdef __getattr__(self, attr):'
  902.       print '\t\td=self.__dict__["_dispobj_"]'
  903.       print '\t\tif d is not None: return getattr(d, attr)'
  904.       print '\t\traise AttributeError, attr'
  905.       print '\tdef __setattr__(self, attr, value):'
  906.       print '\t\tif self.__dict__.has_key(attr): self.__dict__[attr] = value; return'
  907.       print '\t\ttry:'
  908.       print '\t\t\td=self.__dict__["_dispobj_"]'
  909.       print '\t\t\tif d is not None:'
  910.       print '\t\t\t\td.__setattr__(attr, value)'
  911.       print '\t\t\t\treturn'
  912.       print '\t\texcept AttributeError:'
  913.       print '\t\t\tpass'
  914.       print '\t\tself.__dict__[attr] = value'
  915.       print
  916.       self.bHaveWrittenCoClassBaseClass = 1
  917.  
  918.   def checkWriteEventBaseClass(self):
  919.     # Not a base class as such...
  920.       if not self.bHaveWrittenEventBaseClass:
  921.         # Nothing to do any more!
  922.         self.bHaveWrittenEventBaseClass = 1
  923.  
  924. if __name__=='__main__':
  925.   print "This is a worker module.  Please use makepy to generate Python files."
  926.