0001 """genpy.py - The worker for makepy. See makepy.py for more details 0002 0003 This code was moved simply to speed Python in normal circumstances. As the makepy.py 0004 is normally run from the command line, it reparses the code each time. Now makepy 0005 is nothing more than the command line handler and public interface. 0006 0007 The makepy command line etc handling is also getting large enough in its own right! 0008 """ 0009 0010 # NOTE - now supports a "demand" mechanism - the top-level is a package, and 0011 # each class etc can be made individually. 0012 # This should eventually become the default. 0013 # Then the old non-package technique should be removed. 0014 # There should be no b/w compat issues, and will just help clean the code. 0015 # This will be done once the new "demand" mechanism gets a good workout. 0016 import os 0017 import sys 0018 import string 0019 import time 0020 import win32com 0021 0022 import pythoncom 0023 import build 0024 0025 error = "makepy.error" 0026 makepy_version = "0.4.91" # Written to generated file. 0027 0028 GEN_FULL="full" 0029 GEN_DEMAND_BASE = "demand(base)" 0030 GEN_DEMAND_CHILD = "demand(child)" 0031 0032 try: 0033 TrueRepr = repr(True) 0034 FalseRepr = repr(False) 0035 except NameError: 0036 TrueRepr = "1" 0037 FalseRepr = "0" 0038 0039 # This map is used purely for the users benefit -it shows the 0040 # raw, underlying type of Alias/Enums, etc. The COM implementation 0041 # does not use this map at runtime - all Alias/Enum have already 0042 # been translated. 0043 mapVTToTypeString = { 0044 pythoncom.VT_I2: 'types.IntType', 0045 pythoncom.VT_I4: 'types.IntType', 0046 pythoncom.VT_R4: 'types.FloatType', 0047 pythoncom.VT_R8: 'types.FloatType', 0048 pythoncom.VT_BSTR: 'types.StringType', 0049 pythoncom.VT_BOOL: 'types.IntType', 0050 pythoncom.VT_VARIANT: 'types.TypeType', 0051 pythoncom.VT_I1: 'types.IntType', 0052 pythoncom.VT_UI1: 'types.IntType', 0053 pythoncom.VT_UI2: 'types.IntType', 0054 pythoncom.VT_UI4: 'types.IntType', 0055 pythoncom.VT_I8: 'types.LongType', 0056 pythoncom.VT_UI8: 'types.LongType', 0057 pythoncom.VT_INT: 'types.IntType', 0058 pythoncom.VT_DATE: 'pythoncom.PyTimeType', 0059 pythoncom.VT_UINT: 'types.IntType', 0060 } 0061 0062 # Given a propget function's arg desc, return the default parameters for all 0063 # params bar the first. Eg, then Python does a: 0064 # object.Property = "foo" 0065 # Python can only pass the "foo" value. If the property has 0066 # multiple args, and the rest have default values, this allows 0067 # Python to correctly pass those defaults. 0068 def MakeDefaultArgsForPropertyPut(argsDesc): 0069 ret = [] 0070 for desc in argsDesc[1:]: 0071 default = build.MakeDefaultArgRepr(desc) 0072 if default is None: 0073 break 0074 ret.append(default) 0075 return tuple(ret) 0076 0077 0078 def MakeMapLineEntry(dispid, wFlags, retType, argTypes, user, resultCLSID): 0079 # Strip the default value 0080 argTypes = tuple(map(lambda what: what[:2], argTypes)) 0081 return '(%s, %d, %s, %s, "%s", %s)' % \ 0082 (dispid, wFlags, retType[:2], argTypes, user, resultCLSID) 0083 0084 def MakeEventMethodName(eventName): 0085 if eventName[:2]=="On": 0086 return eventName 0087 else: 0088 return "On"+eventName 0089 0090 def WriteSinkEventMap(obj, stream): 0091 print >> stream, '\t_dispid_to_func_ = {' 0092 for name, entry in obj.propMapGet.items() + obj.propMapPut.items() + obj.mapFuncs.items(): 0093 fdesc = entry.desc 0094 print >> stream, '\t\t%9d : "%s",' % (entry.desc[0], MakeEventMethodName(entry.names[0])) 0095 print >> stream, '\t\t}' 0096 0097 0098 # MI is used to join my writable helpers, and the OLE 0099 # classes. 0100 class WritableItem: 0101 def __cmp__(self, other): 0102 "Compare for sorting" 0103 ret = cmp(self.order, other.order) 0104 if ret==0 and self.doc: ret = cmp(self.doc[0], other.doc[0]) 0105 return ret 0106 def __repr__(self): 0107 return "OleItem: doc=%s, order=%d" % (`self.doc`, self.order) 0108 0109 0110 class RecordItem(build.OleItem, WritableItem): 0111 order = 9 0112 typename = "RECORD" 0113 0114 def __init__(self, typeInfo, typeAttr, doc=None, bForUser=1): 0115 ## sys.stderr.write("Record %s: size %s\n" % (doc,typeAttr.cbSizeInstance)) 0116 ## sys.stderr.write(" cVars = %s\n" % (typeAttr.cVars,)) 0117 ## for i in range(typeAttr.cVars): 0118 ## vdesc = typeInfo.GetVarDesc(i) 0119 ## sys.stderr.write(" Var %d has value %s, type %d, desc=%s\n" % (i, vdesc.value, vdesc.varkind, vdesc.elemdescVar)) 0120 ## sys.stderr.write(" Doc is %s\n" % (typeInfo.GetDocumentation(vdesc.memid),)) 0121 0122 build.OleItem.__init__(self, doc) 0123 self.clsid = typeAttr[0] 0124 0125 def WriteClass(self, generator): 0126 pass 0127 0128 # Given an enum, write all aliases for it. 0129 # (no longer necessary for new style code, but still used for old code. 0130 def WriteAliasesForItem(item, aliasItems, stream): 0131 for alias in aliasItems.values(): 0132 if item.doc and alias.aliasDoc and (alias.aliasDoc[0]==item.doc[0]): 0133 alias.WriteAliasItem(aliasItems, stream) 0134 0135 class AliasItem(build.OleItem, WritableItem): 0136 order = 2 0137 typename = "ALIAS" 0138 0139 def __init__(self, typeinfo, attr, doc=None, bForUser = 1): 0140 build.OleItem.__init__(self, doc) 0141 0142 ai = attr[14] 0143 self.attr = attr 0144 if type(ai) == type(()) and \ 0145 type(ai[1])==type(0): # XXX - This is a hack - why tuples? Need to resolve? 0146 href = ai[1] 0147 alinfo = typeinfo.GetRefTypeInfo(href) 0148 self.aliasDoc = alinfo.GetDocumentation(-1) 0149 self.aliasAttr = alinfo.GetTypeAttr() 0150 else: 0151 self.aliasDoc = None 0152 self.aliasAttr = None 0153 0154 def WriteAliasItem(self, aliasDict, stream): 0155 # we could have been written as part of an alias dependency 0156 if self.bWritten: 0157 return 0158 0159 if self.aliasDoc: 0160 depName = self.aliasDoc[0] 0161 if aliasDict.has_key(depName): 0162 aliasDict[depName].WriteAliasItem(aliasDict, stream) 0163 print >> stream, self.doc[0] + " = " + depName 0164 else: 0165 ai = self.attr[14] 0166 if type(ai) == type(0): 0167 try: 0168 typeStr = mapVTToTypeString[ai] 0169 print >> stream, "# %s=%s" % (self.doc[0], typeStr) 0170 except KeyError: 0171 print >> stream, self.doc[0] + " = None # Can't convert alias info " + str(ai) 0172 print >> stream 0173 self.bWritten = 1 0174 0175 class EnumerationItem(build.OleItem, WritableItem): 0176 order = 1 0177 typename = "ENUMERATION" 0178 0179 def __init__(self, typeinfo, attr, doc=None, bForUser=1): 0180 build.OleItem.__init__(self, doc) 0181 0182 self.clsid = attr[0] 0183 self.mapVars = {} 0184 typeFlags = attr[11] 0185 self.hidden = typeFlags & pythoncom.TYPEFLAG_FHIDDEN or \ 0186 typeFlags & pythoncom.TYPEFLAG_FRESTRICTED 0187 0188 for j in range(attr[7]): 0189 vdesc = typeinfo.GetVarDesc(j) 0190 name = typeinfo.GetNames(vdesc[0])[0] 0191 self.mapVars[name] = build.MapEntry(vdesc) 0192 0193 ## def WriteEnumerationHeaders(self, aliasItems, stream): 0194 ## enumName = self.doc[0] 0195 ## print >> stream "%s=constants # Compatibility with previous versions." % (enumName) 0196 ## WriteAliasesForItem(self, aliasItems) 0197 0198 def WriteEnumerationItems(self, stream): 0199 enumName = self.doc[0] 0200 # Write in name alpha order 0201 names = self.mapVars.keys() 0202 names.sort() 0203 for name in names: 0204 entry = self.mapVars[name] 0205 vdesc = entry.desc 0206 if vdesc[4] == pythoncom.VAR_CONST: 0207 val = vdesc[1] 0208 if type(val)==type(0): 0209 if val==0x80000000L: # special case 0210 use = "0x80000000L" # 'L' for future warning 0211 elif val > 0x80000000L or val < 0: # avoid a FutureWarning 0212 use = long(val) 0213 else: 0214 use = hex(val) 0215 else: 0216 use = repr(str(val)) 0217 print >> stream, "\t%-30s=%-10s # from enum %s" % \ 0218 (build.MakePublicAttributeName(name, True), use, enumName) 0219 0220 class VTableItem(build.VTableItem, WritableItem): 0221 order = 4 0222 0223 def WriteClass(self, generator): 0224 self.WriteVTableMap(generator) 0225 self.bWritten = 1 0226 0227 def WriteVTableMap(self, generator): 0228 stream = generator.file 0229 print >> stream, "%s_vtables_dispatch_ = %d" % (self.python_name, self.bIsDispatch) 0230 print >> stream, "%s_vtables_ = [" % (self.python_name, ) 0231 for v in self.vtableFuncs: 0232 chunks = [] 0233 names, dispid, desc = v 0234 arg_desc = desc[2] 0235 0236 arg_reprs = [] 0237 for arg in arg_desc: 0238 defval = build.MakeDefaultArgRepr(arg) 0239 if arg[3] is None: 0240 arg3_repr = None 0241 else: 0242 arg3_repr = repr(arg[3]) 0243 arg_reprs.append((arg[0], arg[1], defval, arg3_repr)) 0244 desc = desc[:2] + (arg_reprs,) + desc[3:] 0245 chunks.append("\t(%r, %d, %r)," % (names, dispid, desc)) 0246 print >> stream, "".join(chunks) 0247 print >> stream, "]" 0248 print >> stream 0249 0250 class DispatchItem(build.DispatchItem, WritableItem): 0251 order = 3 0252 0253 def __init__(self, typeinfo, attr, doc=None): 0254 build.DispatchItem.__init__(self, typeinfo, attr, doc) 0255 self.type_attr = attr 0256 self.coclass_clsid = None 0257 0258 def WriteClass(self, generator): 0259 if not self.bIsDispatch and not self.type_attr.typekind == pythoncom.TKIND_DISPATCH: 0260 return 0261 # This is pretty screwey - now we have vtable support we 0262 # should probably rethink this (ie, maybe write both sides for sinks, etc) 0263 if self.bIsSink: 0264 self.WriteEventSinkClassHeader(generator) 0265 self.WriteCallbackClassBody(generator) 0266 else: 0267 self.WriteClassHeader(generator) 0268 self.WriteClassBody(generator) 0269 print >> generator.file 0270 self.bWritten = 1 0271 0272 def WriteClassHeader(self, generator): 0273 generator.checkWriteDispatchBaseClass() 0274 doc = self.doc 0275 stream = generator.file 0276 print >> stream, 'class ' + self.python_name + '(DispatchBaseClass):' 0277 if doc[1]: print >> stream, '\t' + build._safeQuotedString(doc[1]) 0278 try: 0279 progId = pythoncom.ProgIDFromCLSID(self.clsid) 0280 print >> stream, "\t# This class is creatable by the name '%s'" % (progId) 0281 except pythoncom.com_error: 0282 pass 0283 print >> stream, "\tCLSID = " + repr(self.clsid) 0284 if self.coclass_clsid is None: 0285 print >> stream, "\tcoclass_clsid = None" 0286 else: 0287 print >> stream, "\tcoclass_clsid = " + repr(self.coclass_clsid) 0288 print >> stream 0289 self.bWritten = 1 0290 0291 def WriteEventSinkClassHeader(self, generator): 0292 generator.checkWriteEventBaseClass() 0293 doc = self.doc 0294 stream = generator.file 0295 print >> stream, 'class ' + self.python_name + ':' 0296 if doc[1]: print >> stream, '\t' + build._safeQuotedString(doc[1]) 0297 try: 0298 progId = pythoncom.ProgIDFromCLSID(self.clsid) 0299 print >> stream, "\t# This class is creatable by the name '%s'" % (progId) 0300 except pythoncom.com_error: 0301 pass 0302 print >> stream, '\tCLSID = CLSID_Sink = ' + repr(self.clsid) 0303 if self.coclass_clsid is None: 0304 print >> stream, "\tcoclass_clsid = None" 0305 else: 0306 print >> stream, "\tcoclass_clsid = " + repr(self.coclass_clsid) 0307 print >> stream, '\t_public_methods_ = [] # For COM Server support' 0308 WriteSinkEventMap(self, stream) 0309 print >> stream 0310 print >> stream, '\tdef __init__(self, oobj = None):' 0311 print >> stream, "\t\tif oobj is None:" 0312 print >> stream, "\t\t\tself._olecp = None" 0313 print >> stream, "\t\telse:" 0314 print >> stream, '\t\t\timport win32com.server.util' 0315 print >> stream, '\t\t\tfrom win32com.server.policy import EventHandlerPolicy' 0316 print >> stream, '\t\t\tcpc=oobj._oleobj_.QueryInterface(pythoncom.IID_IConnectionPointContainer)' 0317 print >> stream, '\t\t\tcp=cpc.FindConnectionPoint(self.CLSID_Sink)' 0318 print >> stream, '\t\t\tcookie=cp.Advise(win32com.server.util.wrap(self, usePolicy=EventHandlerPolicy))' 0319 print >> stream, '\t\t\tself._olecp,self._olecp_cookie = cp,cookie' 0320 print >> stream, '\tdef __del__(self):' 0321 print >> stream, '\t\ttry:' 0322 print >> stream, '\t\t\tself.close()' 0323 print >> stream, '\t\texcept pythoncom.com_error:' 0324 print >> stream, '\t\t\tpass' 0325 print >> stream, '\tdef close(self):' 0326 print >> stream, '\t\tif self._olecp is not None:' 0327 print >> stream, '\t\t\tcp,cookie,self._olecp,self._olecp_cookie = self._olecp,self._olecp_cookie,None,None' 0328 print >> stream, '\t\t\tcp.Unadvise(cookie)' 0329 print >> stream, '\tdef _query_interface_(self, iid):' 0330 print >> stream, '\t\timport win32com.server.util' 0331 print >> stream, '\t\tif iid==self.CLSID_Sink: return win32com.server.util.wrap(self)' 0332 print >> stream 0333 self.bWritten = 1 0334 0335 def WriteCallbackClassBody(self, generator): 0336 stream = generator.file 0337 print >> stream, "\t# Event Handlers" 0338 print >> stream, "\t# If you create handlers, they should have the following prototypes:" 0339 for name, entry in self.propMapGet.items() + self.propMapPut.items() + self.mapFuncs.items(): 0340 fdesc = entry.desc 0341 methName = MakeEventMethodName(entry.names[0]) 0342 print >> stream, '#\tdef ' + methName + '(self' + build.BuildCallList(fdesc, entry.names, "defaultNamedOptArg", "defaultNamedNotOptArg","defaultUnnamedArg", "pythoncom.Missing") + '):' 0343 if entry.doc and entry.doc[1]: 0344 print >> stream, '#\t\t' + build._safeQuotedString(entry.doc[1]) 0345 print >> stream 0346 self.bWritten = 1 0347 0348 def WriteClassBody(self, generator): 0349 stream = generator.file 0350 # Write in alpha order. 0351 names = self.mapFuncs.keys() 0352 names.sort() 0353 specialItems = {"count":None, "item":None,"value":None,"_newenum":None} # If found, will end up with (entry, invoke_tupe) 0354 itemCount = None 0355 for name in names: 0356 entry=self.mapFuncs[name] 0357 # skip [restricted] methods, unless it is the 0358 # enumerator (which, being part of the "system", 0359 # we know about and can use) 0360 dispid = entry.desc[0] 0361 if entry.desc[9] & pythoncom.FUNCFLAG_FRESTRICTED and \ 0362 dispid != pythoncom.DISPID_NEWENUM: 0363 continue 0364 # If not accessible via IDispatch, then we can't use it here. 0365 if entry.desc[3] != pythoncom.FUNC_DISPATCH: 0366 continue 0367 if dispid==pythoncom.DISPID_VALUE: 0368 lkey = "value" 0369 elif dispid==pythoncom.DISPID_NEWENUM: 0370 specialItems["_newenum"] = (entry, entry.desc[4], None) 0371 continue # Dont build this one now! 0372 else: 0373 lkey = string.lower(name) 0374 if specialItems.has_key(lkey) and specialItems[lkey] is None: # remember if a special one. 0375 specialItems[lkey] = (entry, entry.desc[4], None) 0376 if generator.bBuildHidden or not entry.hidden: 0377 if entry.GetResultName(): 0378 print >> stream, '\t# Result is of type ' + entry.GetResultName() 0379 if entry.wasProperty: 0380 print >> stream, '\t# The method %s is actually a property, but must be used as a method to correctly pass the arguments' % name 0381 ret = self.MakeFuncMethod(entry,build.MakePublicAttributeName(name)) 0382 for line in ret: 0383 print >> stream, line 0384 print >> stream, "\t_prop_map_get_ = {" 0385 names = self.propMap.keys(); names.sort() 0386 for key in names: 0387 entry = self.propMap[key] 0388 if generator.bBuildHidden or not entry.hidden: 0389 resultName = entry.GetResultName() 0390 if resultName: 0391 print >> stream, "\t\t# Property '%s' is an object of type '%s'" % (key, resultName) 0392 lkey = string.lower(key) 0393 details = entry.desc 0394 resultDesc = details[2] 0395 argDesc = () 0396 mapEntry = MakeMapLineEntry(details[0], pythoncom.DISPATCH_PROPERTYGET, resultDesc, argDesc, key, entry.GetResultCLSIDStr()) 0397 0398 if entry.desc[0]==pythoncom.DISPID_VALUE: 0399 lkey = "value" 0400 elif entry.desc[0]==pythoncom.DISPID_NEWENUM: 0401 lkey = "_newenum" 0402 else: 0403 lkey = string.lower(key) 0404 if specialItems.has_key(lkey) and specialItems[lkey] is None: # remember if a special one. 0405 specialItems[lkey] = (entry, pythoncom.DISPATCH_PROPERTYGET, mapEntry) 0406 # All special methods, except _newenum, are written 0407 # "normally". This is a mess! 0408 if entry.desc[0]==pythoncom.DISPID_NEWENUM: 0409 continue 0410 0411 print >> stream, '\t\t"%s": %s,' % (key, mapEntry) 0412 names = self.propMapGet.keys(); names.sort() 0413 for key in names: 0414 entry = self.propMapGet[key] 0415 if generator.bBuildHidden or not entry.hidden: 0416 if entry.GetResultName(): 0417 print >> stream, "\t\t# Method '%s' returns object of type '%s'" % (key, entry.GetResultName()) 0418 details = entry.desc 0419 lkey = string.lower(key) 0420 argDesc = details[2] 0421 resultDesc = details[8] 0422 mapEntry = MakeMapLineEntry(details[0], pythoncom.DISPATCH_PROPERTYGET, resultDesc, argDesc, key, entry.GetResultCLSIDStr()) 0423 if entry.desc[0]==pythoncom.DISPID_VALUE: 0424 lkey = "value" 0425 elif entry.desc[0]==pythoncom.DISPID_NEWENUM: 0426 lkey = "_newenum" 0427 else: 0428 lkey = string.lower(key) 0429 if specialItems.has_key(lkey) and specialItems[lkey] is None: # remember if a special one. 0430 specialItems[lkey]=(entry, pythoncom.DISPATCH_PROPERTYGET, mapEntry) 0431 # All special methods, except _newenum, are written 0432 # "normally". This is a mess! 0433 if entry.desc[0]==pythoncom.DISPID_NEWENUM: 0434 continue 0435 print >> stream, '\t\t"%s": %s,' % (key, mapEntry) 0436 0437 print >> stream, "\t}" 0438 0439 print >> stream, "\t_prop_map_put_ = {" 0440 # These are "Invoke" args 0441 names = self.propMap.keys(); names.sort() 0442 for key in names: 0443 entry = self.propMap[key] 0444 if generator.bBuildHidden or not entry.hidden: 0445 lkey=string.lower(key) 0446 details = entry.desc 0447 # If default arg is None, write an empty tuple 0448 defArgDesc = build.MakeDefaultArgRepr(details[2]) 0449 if defArgDesc is None: 0450 defArgDesc = "" 0451 else: 0452 defArgDesc = defArgDesc + "," 0453 print >> stream, '\t\t"%s" : ((%s, LCID, %d, 0),(%s)),' % (key, details[0], pythoncom.DISPATCH_PROPERTYPUT, defArgDesc) 0454 0455 names = self.propMapPut.keys(); names.sort() 0456 for key in names: 0457 entry = self.propMapPut[key] 0458 if generator.bBuildHidden or not entry.hidden: 0459 details = entry.desc 0460 defArgDesc = MakeDefaultArgsForPropertyPut(details[2]) 0461 print >> stream, '\t\t"%s": ((%s, LCID, %d, 0),%s),' % (key, details[0], details[4], defArgDesc) 0462 print >> stream, "\t}" 0463 0464 if specialItems["value"]: 0465 entry, invoketype, propArgs = specialItems["value"] 0466 if propArgs is None: 0467 typename = "method" 0468 ret = self.MakeFuncMethod(entry,'__call__') 0469 else: 0470 typename = "property" 0471 ret = [ "\tdef __call__(self):\n\t\treturn self._ApplyTypes_(*%s)" % propArgs] 0472 print >> stream, "\t# Default %s for this class is '%s'" % (typename, entry.names[0]) 0473 for line in ret: 0474 print >> stream, line 0475 print >> stream, "\t# str(ob) and int(ob) will use __call__" 0476 print >> stream, "\tdef __unicode__(self, *args):" 0477 print >> stream, "\t\ttry:" 0478 print >> stream, "\t\t\treturn unicode(self.__call__(*args))" 0479 print >> stream, "\t\texcept pythoncom.com_error:" 0480 print >> stream, "\t\t\treturn repr(self)" 0481 print >> stream, "\tdef __str__(self, *args):" 0482 print >> stream, "\t\treturn str(self.__unicode__(*args))" 0483 print >> stream, "\tdef __int__(self, *args):" 0484 print >> stream, "\t\treturn int(self.__call__(*args))" 0485 0486 0487 if specialItems["_newenum"]: 0488 enumEntry, invoketype, propArgs = specialItems["_newenum"] 0489 resultCLSID = enumEntry.GetResultCLSIDStr() 0490 # If we dont have a good CLSID for the enum result, assume it is the same as the Item() method. 0491 if resultCLSID == "None" and self.mapFuncs.has_key("Item"): 0492 resultCLSID = self.mapFuncs["Item"].GetResultCLSIDStr() 0493 # "Native" Python iterator support 0494 print >> stream, '\tdef __iter__(self):' 0495 print >> stream, '\t\t"Return a Python iterator for this object"' 0496 print >> stream, '\t\tob = self._oleobj_.InvokeTypes(%d,LCID,%d,(13, 10),())' % (pythoncom.DISPID_NEWENUM, enumEntry.desc[4]) 0497 print >> stream, '\t\treturn win32com.client.util.Iterator(ob)' 0498 # And 'old style' iterator support - magically used to simulate iterators 0499 # before Python grew them 0500 print >> stream, '\tdef _NewEnum(self):' 0501 print >> stream, '\t\t"Create an enumerator from this object"' 0502 print >> stream, '\t\treturn win32com.client.util.WrapEnum(self._oleobj_.InvokeTypes(%d,LCID,%d,(13, 10),()),%s)' % (pythoncom.DISPID_NEWENUM, enumEntry.desc[4], resultCLSID) 0503 print >> stream, '\tdef __getitem__(self, index):' 0504 print >> stream, '\t\t"Allow this class to be accessed as a collection"' 0505 print >> stream, "\t\tif not self.__dict__.has_key('_enum_'):" 0506 print >> stream, "\t\t\tself.__dict__['_enum_'] = self._NewEnum()" 0507 print >> stream, "\t\treturn self._enum_.__getitem__(index)" 0508 else: # Not an Enumerator, but may be an "Item/Count" based collection 0509 if specialItems["item"]: 0510 entry, invoketype, propArgs = specialItems["item"] 0511 print >> stream, '\t#This class has Item property/method which may take args - allow indexed access' 0512 print >> stream, '\tdef __getitem__(self, item):' 0513 print >> stream, '\t\treturn self._get_good_object_(self._oleobj_.Invoke(*(%d, LCID, %d, 1, item)), "Item")' % (entry.desc[0], invoketype) 0514 if specialItems["count"]: 0515 entry, invoketype, propArgs = specialItems["count"] 0516 if propArgs is None: 0517 typename = "method" 0518 ret = self.MakeFuncMethod(entry,'__len__') 0519 else: 0520 typename = "property" 0521 ret = [ "\tdef __len__(self):\n\t\treturn self._ApplyTypes_(*%s)" % propArgs] 0522 print >> stream, "\t#This class has Count() %s - allow len(ob) to provide this" % (typename) 0523 for line in ret: 0524 print >> stream, line 0525 # Also include a __nonzero__ 0526 print >> stream, "\t#This class has a __len__ - this is needed so 'if object:' always returns TRUE." 0527 print >> stream, "\tdef __nonzero__(self):" 0528 print >> stream, "\t\treturn %s" % (TrueRepr,) 0529 0530 class CoClassItem(build.OleItem, WritableItem): 0531 order = 5 0532 typename = "COCLASS" 0533 0534 def __init__(self, typeinfo, attr, doc=None, sources = [], interfaces = [], bForUser=1): 0535 build.OleItem.__init__(self, doc) 0536 self.clsid = attr[0] 0537 self.sources = sources 0538 self.interfaces = interfaces 0539 self.bIsDispatch = 1 # Pretend it is so it is written to the class map. 0540 0541 def WriteClass(self, generator): 0542 generator.checkWriteCoClassBaseClass() 0543 doc = self.doc 0544 stream = generator.file 0545 if generator.generate_type == GEN_DEMAND_CHILD: 0546 # Some special imports we must setup. 0547 referenced_items = [] 0548 for ref, flag in self.sources: 0549 referenced_items.append(ref) 0550 for ref, flag in self.interfaces: 0551 referenced_items.append(ref) 0552 print >> stream, "import sys" 0553 for ref in referenced_items: 0554 print >> stream, "__import__('%s.%s')" % (generator.base_mod_name, ref.python_name) 0555 print >> stream, "%s = sys.modules['%s.%s'].%s" % (ref.python_name, generator.base_mod_name, ref.python_name, ref.python_name) 0556 # And pretend we have written it - the name is now available as if we had! 0557 ref.bWritten = 1 0558 try: 0559 progId = pythoncom.ProgIDFromCLSID(self.clsid) 0560 print >> stream, "# This CoClass is known by the name '%s'" % (progId) 0561 except pythoncom.com_error: 0562 pass 0563 print >> stream, 'class %s(CoClassBaseClass): # A CoClass' % (self.python_name) 0564 if doc and doc[1]: print >> stream, '\t# ' + doc[1] 0565 print >> stream, '\tCLSID = %r' % (self.clsid,) 0566 print >> stream, '\tcoclass_sources = [' 0567 defItem = None 0568 for item, flag in self.sources: 0569 if flag & pythoncom.IMPLTYPEFLAG_FDEFAULT: 0570 defItem = item 0571 # If we have written a Python class, reference the name - 0572 # otherwise just the IID. 0573 if item.bWritten: key = item.python_name 0574 else: key = repr(str(item.clsid)) # really the iid. 0575 print >> stream, '\t\t%s,' % (key) 0576 print >> stream, '\t]' 0577 if defItem: 0578 if defItem.bWritten: defName = defItem.python_name 0579 else: defName = repr(str(defItem.clsid)) # really the iid. 0580 print >> stream, '\tdefault_source = %s' % (defName,) 0581 print >> stream, '\tcoclass_interfaces = [' 0582 defItem = None 0583 for item, flag in self.interfaces: 0584 if flag & pythoncom.IMPLTYPEFLAG_FDEFAULT: # and dual: 0585 defItem = item 0586 # If we have written a class, refeence its name, otherwise the IID 0587 if item.bWritten: key = item.python_name 0588 else: key = repr(str(item.clsid)) # really the iid. 0589 print >> stream, '\t\t%s,' % (key,) 0590 print >> stream, '\t]' 0591 if defItem: 0592 if defItem.bWritten: defName = defItem.python_name 0593 else: defName = repr(str(defItem.clsid)) # really the iid. 0594 print >> stream, '\tdefault_interface = %s' % (defName,) 0595 self.bWritten = 1 0596 print >> stream 0597 0598 class GeneratorProgress: 0599 def __init__(self): 0600 pass 0601 def Starting(self, tlb_desc): 0602 """Called when the process starts. 0603 """ 0604 self.tlb_desc = tlb_desc 0605 def Finished(self): 0606 """Called when the process is complete. 0607 """ 0608 def SetDescription(self, desc, maxticks = None): 0609 """We are entering a major step. If maxticks, then this 0610 is how many ticks we expect to make until finished 0611 """ 0612 def Tick(self, desc = None): 0613 """Minor progress step. Can provide new description if necessary 0614 """ 0615 def VerboseProgress(self, desc): 0616 """Verbose/Debugging output. 0617 """ 0618 def LogWarning(self, desc): 0619 """If a warning is generated 0620 """ 0621 def LogBeginGenerate(self, filename): 0622 pass 0623 def Close(self): 0624 pass 0625 0626 class Generator: 0627 def __init__(self, typelib, sourceFilename, progressObject, bBuildHidden=1, bUnicodeToString=0): 0628 self.bHaveWrittenDispatchBaseClass = 0 0629 self.bHaveWrittenCoClassBaseClass = 0 0630 self.bHaveWrittenEventBaseClass = 0 0631 self.typelib = typelib 0632 self.sourceFilename = sourceFilename 0633 self.bBuildHidden = bBuildHidden 0634 self.bUnicodeToString = bUnicodeToString 0635 self.progress = progressObject 0636 # These 2 are later additions and most of the code still 'print's... 0637 self.file = None 0638 0639 def CollectOleItemInfosFromType(self): 0640 ret = [] 0641 for i in xrange(self.typelib.GetTypeInfoCount()): 0642 info = self.typelib.GetTypeInfo(i) 0643 infotype = self.typelib.GetTypeInfoType(i) 0644 doc = self.typelib.GetDocumentation(i) 0645 attr = info.GetTypeAttr() 0646 ret.append((info, infotype, doc, attr)) 0647 return ret 0648 0649 def _Build_CoClass(self, type_info_tuple): 0650 info, infotype, doc, attr = type_info_tuple 0651 # find the source and dispinterfaces for the coclass 0652 child_infos = [] 0653 for j in range(attr[8]): 0654 flags = info.GetImplTypeFlags(j) 0655 refType = info.GetRefTypeInfo(info.GetRefTypeOfImplType(j)) 0656 refAttr = refType.GetTypeAttr() 0657 child_infos.append( (info, refAttr.typekind, refType, refType.GetDocumentation(-1), refAttr, flags) ) 0658 0659 # Done generating children - now the CoClass itself. 0660 newItem = CoClassItem(info, attr, doc) 0661 return newItem, child_infos 0662 0663 def _Build_CoClassChildren(self, coclass, coclass_info, oleItems, vtableItems): 0664 sources = {} 0665 interfaces = {} 0666 for info, info_type, refType, doc, refAttr, flags in coclass_info: 0667 # 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)) 0668 if refAttr.typekind == pythoncom.TKIND_DISPATCH: 0669 clsid = refAttr[0] 0670 if oleItems.has_key(clsid): 0671 dispItem = oleItems[clsid] 0672 else: 0673 dispItem = DispatchItem(refType, refAttr, doc) 0674 oleItems[dispItem.clsid] = dispItem 0675 dispItem.coclass_clsid = coclass.clsid 0676 if flags & pythoncom.IMPLTYPEFLAG_FSOURCE: 0677 dispItem.bIsSink = 1 0678 sources[dispItem.clsid] = (dispItem, flags) 0679 else: 0680 interfaces[dispItem.clsid] = (dispItem, flags) 0681 # If dual interface, make do that too. 0682 if not vtableItems.has_key(clsid) and refAttr[11] & pythoncom.TYPEFLAG_FDUAL: 0683 refType = refType.GetRefTypeInfo(refType.GetRefTypeOfImplType(-1)) 0684 refAttr = refType.GetTypeAttr() 0685 assert refAttr.typekind == pythoncom.TKIND_INTERFACE, "must be interface bynow!" 0686 vtableItem = VTableItem(refType, refAttr, doc) 0687 vtableItems[clsid] = vtableItem 0688 coclass.sources = sources.values() 0689 coclass.interfaces = interfaces.values() 0690 0691 def _Build_Interface(self, type_info_tuple): 0692 info, infotype, doc, attr = type_info_tuple 0693 oleItem = vtableItem = None 0694 if infotype == pythoncom.TKIND_DISPATCH: 0695 oleItem = DispatchItem(info, attr, doc) 0696 # If this DISPATCH interface dual, then build that too. 0697 if (attr.wTypeFlags & pythoncom.TYPEFLAG_FDUAL): 0698 # sys.stderr.write("interface " + doc[0] + " is not dual\n"); 0699 # Get the vtable interface 0700 refhtype = info.GetRefTypeOfImplType(-1) 0701 info = info.GetRefTypeInfo(refhtype) 0702 attr = info.GetTypeAttr() 0703 infotype = pythoncom.TKIND_INTERFACE 0704 else: 0705 infotype = None 0706 assert infotype in [None, pythoncom.TKIND_INTERFACE], "Must be a real interface at this point" 0707 if infotype == pythoncom.TKIND_INTERFACE: 0708 vtableItem = VTableItem(info, attr, doc) 0709 return oleItem, vtableItem 0710 0711 def BuildOleItemsFromType(self): 0712 assert self.bBuildHidden, "This code doesnt look at the hidden flag - I thought everyone set it true!?!?!" 0713 oleItems = {} 0714 enumItems = {} 0715 recordItems = {} 0716 vtableItems = {} 0717 0718 for type_info_tuple in self.CollectOleItemInfosFromType(): 0719 info, infotype, doc, attr = type_info_tuple 0720 clsid = attr[0] 0721 if infotype == pythoncom.TKIND_ENUM or infotype == pythoncom.TKIND_MODULE: 0722 newItem = EnumerationItem(info, attr, doc) 0723 enumItems[newItem.doc[0]] = newItem 0724 # We never hide interfaces (MSAccess, for example, nominates interfaces as 0725 # hidden, assuming that you only ever use them via the CoClass) 0726 elif infotype in [pythoncom.TKIND_DISPATCH, pythoncom.TKIND_INTERFACE]: 0727 if not oleItems.has_key(clsid): 0728 oleItem, vtableItem = self._Build_Interface(type_info_tuple) 0729 oleItems[clsid] = oleItem # Even "None" goes in here. 0730 if vtableItem is not None: 0731 vtableItems[clsid] = vtableItem 0732 elif infotype == pythoncom.TKIND_RECORD or infotype == pythoncom.TKIND_UNION: 0733 newItem = RecordItem(info, attr, doc) 0734 recordItems[newItem.clsid] = newItem 0735 elif infotype == pythoncom.TKIND_ALIAS: 0736 # We dont care about alias' - handled intrinsicly. 0737 continue 0738 elif infotype == pythoncom.TKIND_COCLASS: 0739 newItem, child_infos = self._Build_CoClass(type_info_tuple) 0740 self._Build_CoClassChildren(newItem, child_infos, oleItems, vtableItems) 0741 oleItems[newItem.clsid] = newItem 0742 else: 0743 self.progress.LogWarning("Unknown TKIND found: %d" % infotype) 0744 0745 return oleItems, enumItems, recordItems, vtableItems 0746 0747 def generate(self, file, is_for_demand = 0): 0748 if is_for_demand: 0749 self.generate_type = GEN_DEMAND_BASE 0750 else: 0751 self.generate_type = GEN_FULL 0752 self.file = file 0753 self.do_generate() 0754 self.file = None 0755 self.progress.Finished() 0756 0757 def do_gen_file_header(self): 0758 la = self.typelib.GetLibAttr() 0759 moduleDoc = self.typelib.GetDocumentation(-1) 0760 docDesc = "" 0761 if moduleDoc[1]: 0762 docDesc = moduleDoc[1] 0763 0764 # Reset all the 'per file' state 0765 self.bHaveWrittenDispatchBaseClass = 0 0766 self.bHaveWrittenCoClassBaseClass = 0 0767 self.bHaveWrittenEventBaseClass = 0 0768 0769 # encodings were giving me grief with McMillan's Installer 0770 # until I get to the bottom of this, don't generate 0771 # a coding line when frozen. 0772 if not hasattr(sys, "frozen") and sys.platform.startswith("win"): 0773 print >> self.file, '# -*- coding: mbcs -*-' # Is this always correct? 0774 print >> self.file, '# Created by makepy.py version %s' % (makepy_version,) 0775 print >> self.file, '# By python version %s' % \ 0776 (sys.version.replace("\n", "-"),) 0777 if self.sourceFilename: 0778 print >> self.file, "# From type library '%s'" % (os.path.split(self.sourceFilename)[1],) 0779 print >> self.file, '# On %s' % time.ctime(time.time()) 0780 0781 print >> self.file, '"""' + docDesc + '"""' 0782 0783 print >> self.file, 'makepy_version =', `makepy_version` 0784 try: 0785 print >> self.file, 'python_version = 0x%x' % (sys.hexversion,) 0786 except AttributeError: 0787 print >> self.file, 'python_version = 0x0 # Presumably Python 1.5.2 - 0x0 is not a problem' 0788 print >> self.file 0789 print >> self.file, 'import win32com.client.CLSIDToClass, pythoncom' 0790 print >> self.file, 'import win32com.client.util' 0791 print >> self.file, 'from pywintypes import IID' 0792 print >> self.file, 'from win32com.client import Dispatch' 0793 print >> self.file 0794 print >> self.file, '# The following 3 lines may need tweaking for the particular server' 0795 print >> self.file, '# Candidates are pythoncom.Missing and pythoncom.Empty' 0796 print >> self.file, 'defaultNamedOptArg=pythoncom.Empty' 0797 print >> self.file, 'defaultNamedNotOptArg=pythoncom.Empty' 0798 print >> self.file, 'defaultUnnamedArg=pythoncom.Empty' 0799 print >> self.file 0800 print >> self.file, 'CLSID = ' + repr(la[0]) 0801 print >> self.file, 'MajorVersion = ' + str(la[3]) 0802 print >> self.file, 'MinorVersion = ' + str(la[4]) 0803 print >> self.file, 'LibraryFlags = ' + str(la[5]) 0804 print >> self.file, 'LCID = ' + hex(la[1]) 0805 print >> self.file 0806 0807 def do_generate(self): 0808 moduleDoc = self.typelib.GetDocumentation(-1) 0809 stream = self.file 0810 docDesc = "" 0811 if moduleDoc[1]: 0812 docDesc = moduleDoc[1] 0813 self.progress.Starting(docDesc) 0814 self.progress.SetDescription("Building definitions from type library...") 0815 0816 self.do_gen_file_header() 0817 0818 oleItems, enumItems, recordItems, vtableItems = self.BuildOleItemsFromType() 0819 0820 self.progress.SetDescription("Generating...", len(oleItems)+len(enumItems)+len(vtableItems)) 0821 0822 # Generate the constants and their support. 0823 if enumItems: 0824 print >> stream, "class constants:" 0825 list = enumItems.values() 0826 list.sort() 0827 for oleitem in list: 0828 oleitem.WriteEnumerationItems(stream) 0829 self.progress.Tick() 0830 print >> stream 0831 0832 if self.generate_type == GEN_FULL: 0833 list = oleItems.values() 0834 list = filter(lambda l: l is not None, list) 0835 list.sort() 0836 for oleitem in list: 0837 self.progress.Tick() 0838 oleitem.WriteClass(self) 0839 0840 list = vtableItems.values() 0841 list.sort() 0842 for oleitem in list: 0843 self.progress.Tick() 0844 oleitem.WriteClass(self) 0845 else: 0846 self.progress.Tick(len(oleItems)+len(vtableItems)) 0847 0848 print >> stream, 'RecordMap = {' 0849 list = recordItems.values() 0850 for record in list: 0851 if str(record.clsid) == pythoncom.IID_NULL: 0852 print >> stream, "\t###%s: %s, # Typedef disabled because it doesn't have a non-null GUID" % (`record.doc[0]`, `str(record.clsid)`) 0853 else: 0854 print >> stream, "\t%s: %s," % (`record.doc[0]`, `str(record.clsid)`) 0855 print >> stream, "}" 0856 print >> stream 0857 0858 # Write out _all_ my generated CLSID's in the map 0859 if self.generate_type == GEN_FULL: 0860 print >> stream, 'CLSIDToClassMap = {' 0861 for item in oleItems.values(): 0862 if item is not None and item.bWritten: 0863 print >> stream, "\t'%s' : %s," % (str(item.clsid), item.python_name) 0864 print >> stream, '}' 0865 print >> stream, 'CLSIDToPackageMap = {}' 0866 print >> stream, 'win32com.client.CLSIDToClass.RegisterCLSIDsFromDict( CLSIDToClassMap )' 0867 print >> stream, "VTablesToPackageMap = {}" 0868 print >> stream, "VTablesToClassMap = {" 0869 for item in vtableItems.values(): 0870 print >> stream, "\t'%s' : '%s'," % (item.clsid,item.python_name) 0871 print >> stream, '}' 0872 print >> stream 0873 0874 else: 0875 print >> stream, 'CLSIDToClassMap = {}' 0876 print >> stream, 'CLSIDToPackageMap = {' 0877 for item in oleItems.values(): 0878 if item is not None: 0879 print >> stream, "\t'%s' : %s," % (str(item.clsid), `item.python_name`) 0880 print >> stream, '}' 0881 print >> stream, "VTablesToClassMap = {}" 0882 print >> stream, "VTablesToPackageMap = {" 0883 for item in vtableItems.values(): 0884 print >> stream, "\t'%s' : '%s'," % (item.clsid,item.python_name) 0885 print >> stream, '}' 0886 print >> stream 0887 0888 print >> stream 0889 # Bit of a hack - build a temp map of iteItems + vtableItems - coClasses 0890 map = {} 0891 for item in oleItems.values(): 0892 if item is not None and not isinstance(item, CoClassItem): 0893 map[item.python_name] = item.clsid 0894 for item in vtableItems.values(): # No nones or CoClasses in this map 0895 map[item.python_name] = item.clsid 0896 0897 print >> stream, "NamesToIIDMap = {" 0898 for name, iid in map.items(): 0899 print >> stream, "\t'%s' : '%s'," % (name, iid) 0900 print >> stream, '}' 0901 print >> stream 0902 0903 if enumItems: 0904 print >> stream, 'win32com.client.constants.__dicts__.append(constants.__dict__)' 0905 print >> stream 0906 0907 def generate_child(self, child, dir): 0908 "Generate a single child. May force a few children to be built as we generate deps" 0909 self.generate_type = GEN_DEMAND_CHILD 0910 0911 la = self.typelib.GetLibAttr() 0912 lcid = la[1] 0913 clsid = la[0] 0914 major=la[3] 0915 minor=la[4] 0916 self.base_mod_name = "win32com.gen_py." + str(clsid)[1:-1] + "x%sx%sx%s" % (lcid, major, minor) 0917 try: 0918 # Process the type library's CoClass objects, looking for the 0919 # specified name, or where a child has the specified name. 0920 # This ensures that all interesting things (including event interfaces) 0921 # are generated correctly. 0922 oleItems = {} 0923 vtableItems = {} 0924 infos = self.CollectOleItemInfosFromType() 0925 found = 0 0926 for type_info_tuple in infos: 0927 info, infotype, doc, attr = type_info_tuple 0928 if infotype == pythoncom.TKIND_COCLASS: 0929 coClassItem, child_infos = self._Build_CoClass(type_info_tuple) 0930 found = build.MakePublicAttributeName(doc[0])==child 0931 if not found: 0932 # OK, check the child interfaces 0933 for info, info_type, refType, doc, refAttr, flags in child_infos: 0934 if build.MakePublicAttributeName(doc[0]) == child: 0935 found = 1 0936 break 0937 if found: 0938 oleItems[coClassItem.clsid] = coClassItem 0939 self._Build_CoClassChildren(coClassItem, child_infos, oleItems, vtableItems) 0940 break 0941 if not found: 0942 # Doesn't appear in a class defn - look in the interface objects for it 0943 for type_info_tuple in infos: 0944 info, infotype, doc, attr = type_info_tuple 0945 if infotype in [pythoncom.TKIND_INTERFACE, pythoncom.TKIND_DISPATCH]: 0946 if build.MakePublicAttributeName(doc[0]) == child: 0947 found = 1 0948 oleItem, vtableItem = self._Build_Interface(type_info_tuple) 0949 oleItems[clsid] = oleItem # Even "None" goes in here. 0950 if vtableItem is not None: 0951 vtableItems[clsid] = vtableItem 0952 0953 assert found, "Cant find the '%s' interface in the CoClasses, or the interfaces" % (child,) 0954 # Make a map of iid: dispitem, vtableitem) 0955 items = {} 0956 for key, value in oleItems.items(): 0957 items[key] = (value,None) 0958 for key, value in vtableItems.items(): 0959 existing = items.get(key, None) 0960 if existing is not None: 0961 new_val = existing[0], value 0962 else: 0963 new_val = None, value 0964 items[key] = new_val 0965 0966 self.progress.SetDescription("Generating...", len(items)) 0967 for oleitem, vtableitem in items.values(): 0968 an_item = oleitem or vtableitem 0969 assert not self.file, "already have a file?" 0970 self.file = open(os.path.join(dir, an_item.python_name) + ".py", "w") 0971 try: 0972 if oleitem is not None: 0973 self.do_gen_child_item(oleitem) 0974 if vtableitem is not None: 0975 self.do_gen_child_item(vtableitem) 0976 self.progress.Tick() 0977 finally: 0978 self.file.close() 0979 self.file = None 0980 finally: 0981 self.progress.Finished() 0982 0983 def do_gen_child_item(self, oleitem): 0984 moduleDoc = self.typelib.GetDocumentation(-1) 0985 docDesc = "" 0986 if moduleDoc[1]: 0987 docDesc = moduleDoc[1] 0988 self.progress.Starting(docDesc) 0989 self.progress.SetDescription("Building definitions from type library...") 0990 self.do_gen_file_header() 0991 oleitem.WriteClass(self) 0992 if oleitem.bWritten: 0993 print >> self.file, 'win32com.client.CLSIDToClass.RegisterCLSID( "%s", %s )' % (oleitem.clsid, oleitem.python_name) 0994 0995 def checkWriteDispatchBaseClass(self): 0996 if not self.bHaveWrittenDispatchBaseClass: 0997 print >> self.file, "from win32com.client import DispatchBaseClass" 0998 self.bHaveWrittenDispatchBaseClass = 1 0999 1000 def checkWriteCoClassBaseClass(self): 1001 if not self.bHaveWrittenCoClassBaseClass: 1002 print >> self.file, "from win32com.client import CoClassBaseClass" 1003 self.bHaveWrittenCoClassBaseClass = 1 1004 1005 def checkWriteEventBaseClass(self): 1006 # Not a base class as such... 1007 if not self.bHaveWrittenEventBaseClass: 1008 # Nothing to do any more! 1009 self.bHaveWrittenEventBaseClass = 1 1010 1011 if __name__=='__main__': 1012 print "This is a worker module. Please use makepy to generate Python files." 1013
Generated by PyXR 0.9.4