codekingpro/portable-devtools
114k
1"""Contains knowledge to build a COM object definition.2 3This module is used by both the @dynamic@ and @makepy@ modules to build4all knowledge of a COM object.5 6This module contains classes which contain the actual knowledge of the object.7This include parameter and return type information, the COM dispid and CLSID, etc.8 9Other modules may use this information to generate .py files, use the information10dynamically, 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.py16#17# OleItem, DispatchItem, MapEntry, BuildCallList() is used by makepy18 19import datetime20import string21import sys22from keyword import iskeyword23 24import pythoncom25import winerror26from pywintypes import TimeType27 28 29# It isn't really clear what the quoting rules are in a C/IDL string and30# literals like a quote char and backslashes makes life a little painful to31# always render the string perfectly - so just punt and fall-back to a repr()32def _makeDocString(s):33 if sys.version_info < (3,):34 s = s.encode("mbcs")35 return repr(s)36 37 38error = "PythonCOM.Client.Build error"39 40 41class NotSupportedException(Exception):42 pass # Raised when we cant support a param type.43 44 45DropIndirection = "DropIndirection"46 47NoTranslateTypes = [48 pythoncom.VT_BOOL,49 pythoncom.VT_CLSID,50 pythoncom.VT_CY,51 pythoncom.VT_DATE,52 pythoncom.VT_DECIMAL,53 pythoncom.VT_EMPTY,54 pythoncom.VT_ERROR,55 pythoncom.VT_FILETIME,56 pythoncom.VT_HRESULT,57 pythoncom.VT_I1,58 pythoncom.VT_I2,59 pythoncom.VT_I4,60 pythoncom.VT_I8,61 pythoncom.VT_INT,62 pythoncom.VT_NULL,63 pythoncom.VT_R4,64 pythoncom.VT_R8,65 pythoncom.VT_NULL,66 pythoncom.VT_STREAM,67 pythoncom.VT_UI1,68 pythoncom.VT_UI2,69 pythoncom.VT_UI4,70 pythoncom.VT_UI8,71 pythoncom.VT_UINT,72 pythoncom.VT_VOID,73]74 75NoTranslateMap = {}76for v in NoTranslateTypes:77 NoTranslateMap[v] = None78 79 80class MapEntry:81 "Simple holder for named attibutes - items in a map."82 83 def __init__(84 self,85 desc_or_id,86 names=None,87 doc=None,88 resultCLSID=pythoncom.IID_NULL,89 resultDoc=None,90 hidden=0,91 ):92 if type(desc_or_id) == type(0):93 self.dispid = desc_or_id94 self.desc = None95 else:96 self.dispid = desc_or_id[0]97 self.desc = desc_or_id98 99 self.names = names100 self.doc = doc101 self.resultCLSID = resultCLSID102 self.resultDocumentation = resultDoc103 self.wasProperty = (104 0 # Have I been transformed into a function so I can pass args?105 )106 self.hidden = hidden107 108 def __repr__(self):109 return (110 "MapEntry(dispid={s.dispid}, desc={s.desc}, names={s.names}, doc={s.doc!r}, "111 "resultCLSID={s.resultCLSID}, resultDocumentation={s.resultDocumentation}, "112 "wasProperty={s.wasProperty}, hidden={s.hidden}"113 ).format(s=self)114 115 def GetResultCLSID(self):116 rc = self.resultCLSID117 if rc == pythoncom.IID_NULL:118 return None119 return rc120 121 # Return a string, suitable for output - either "'{...}'" or "None"122 def GetResultCLSIDStr(self):123 rc = self.GetResultCLSID()124 if rc is None:125 return "None"126 return repr(127 str(rc)128 ) # Convert the IID object to a string, then to a string in a string.129 130 def GetResultName(self):131 if self.resultDocumentation is None:132 return None133 return self.resultDocumentation[0]134 135 136class OleItem:137 typename = "OleItem"138 139 def __init__(self, doc=None):140 self.doc = doc141 if self.doc:142 self.python_name = MakePublicAttributeName(self.doc[0])143 else:144 self.python_name = None145 self.bWritten = 0146 self.bIsDispatch = 0147 self.bIsSink = 0148 self.clsid = None149 self.co_class = None150 151 152class DispatchItem(OleItem):153 typename = "DispatchItem"154 155 def __init__(self, typeinfo=None, attr=None, doc=None, bForUser=1):156 OleItem.__init__(self, doc)157 self.propMap = {}158 self.propMapGet = {}159 self.propMapPut = {}160 self.mapFuncs = {}161 self.defaultDispatchName = None162 self.hidden = 0163 164 if typeinfo:165 self.Build(typeinfo, attr, bForUser)166 167 def _propMapPutCheck_(self, key, item):168 ins, outs, opts = self.CountInOutOptArgs(item.desc[2])169 if ins > 1: # if a Put property takes more than 1 arg:170 if opts + 1 == ins or ins == item.desc[6] + 1:171 newKey = "Set" + key172 deleteExisting = 0 # This one is still OK173 else:174 deleteExisting = 1 # No good to us175 if key in self.mapFuncs or key in self.propMapGet:176 newKey = "Set" + key177 else:178 newKey = key179 item.wasProperty = 1180 self.mapFuncs[newKey] = item181 if deleteExisting:182 del self.propMapPut[key]183 184 def _propMapGetCheck_(self, key, item):185 ins, outs, opts = self.CountInOutOptArgs(item.desc[2])186 if ins > 0: # if a Get property takes _any_ in args:187 if item.desc[6] == ins or ins == opts:188 newKey = "Get" + key189 deleteExisting = 0 # This one is still OK190 else:191 deleteExisting = 1 # No good to us192 if key in self.mapFuncs:193 newKey = "Get" + key194 else:195 newKey = key196 item.wasProperty = 1197 self.mapFuncs[newKey] = item198 if deleteExisting:199 del self.propMapGet[key]200 201 def _AddFunc_(self, typeinfo, fdesc, bForUser):202 assert fdesc.desckind == pythoncom.DESCKIND_FUNCDESC203 id = fdesc.memid204 funcflags = fdesc.wFuncFlags205 try:206 names = typeinfo.GetNames(id)207 name = names[0]208 except pythoncom.ole_error:209 name = ""210 names = None211 212 doc = None213 try:214 if bForUser:215 doc = typeinfo.GetDocumentation(id)216 except pythoncom.ole_error:217 pass218 219 if id == 0 and name:220 self.defaultDispatchName = name221 222 invkind = fdesc.invkind223 224 # We need to translate any Alias', Enums, structs etc in result and args225 typerepr, flag, defval = fdesc.rettype226 # sys.stderr.write("%s result - %s -> " % (name, typerepr))227 typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)228 # sys.stderr.write("%s\n" % (typerepr,))229 fdesc.rettype = typerepr, flag, defval, resultCLSID230 # Translate any Alias or Enums in argument list.231 argList = []232 for argDesc in fdesc.args:233 typerepr, flag, defval = argDesc234 # sys.stderr.write("%s arg - %s -> " % (name, typerepr))235 arg_type, arg_clsid, arg_doc = _ResolveType(typerepr, typeinfo)236 argDesc = arg_type, flag, defval, arg_clsid237 # sys.stderr.write("%s\n" % (argDesc[0],))238 argList.append(argDesc)239 fdesc.args = tuple(argList)240 241 hidden = (funcflags & pythoncom.FUNCFLAG_FHIDDEN) != 0242 if invkind == pythoncom.INVOKE_PROPERTYGET:243 map = self.propMapGet244 # This is not the best solution, but I dont think there is245 # one without specific "set" syntax.246 # If there is a single PUT or PUTREF, it will function as a property.247 # If there are both, then the PUT remains a property, and the PUTREF248 # gets transformed into a function.249 # (in vb, PUT=="obj=other_obj", PUTREF="set obj=other_obj250 elif invkind in (pythoncom.INVOKE_PROPERTYPUT, pythoncom.INVOKE_PROPERTYPUTREF):251 # Special case252 existing = self.propMapPut.get(name, None)253 if existing is not None:254 if existing.desc[4] == pythoncom.INVOKE_PROPERTYPUT: # Keep this one255 map = self.mapFuncs256 name = "Set" + name257 else: # Existing becomes a func.258 existing.wasProperty = 1259 self.mapFuncs["Set" + name] = existing260 map = self.propMapPut # existing gets overwritten below.261 else:262 map = self.propMapPut # first time weve seen it.263 264 elif invkind == pythoncom.INVOKE_FUNC:265 map = self.mapFuncs266 else:267 map = None268 if not map is None:269 # if map.has_key(name):270 # sys.stderr.write("Warning - overwriting existing method/attribute %s\n" % name)271 map[name] = MapEntry(fdesc, names, doc, resultCLSID, resultDoc, hidden)272 # any methods that can't be reached via DISPATCH we return None273 # for, so dynamic dispatch doesnt see it.274 if fdesc.funckind != pythoncom.FUNC_DISPATCH:275 return None276 return (name, map)277 return None278 279 def _AddVar_(self, typeinfo, vardesc, bForUser):280 ### need pythoncom.VARFLAG_FRESTRICTED ...281 ### then check it282 assert vardesc.desckind == pythoncom.DESCKIND_VARDESC283 284 if vardesc.varkind == pythoncom.VAR_DISPATCH:285 id = vardesc.memid286 names = typeinfo.GetNames(id)287 # Translate any Alias or Enums in result.288 typerepr, flags, defval = vardesc.elemdescVar289 typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)290 vardesc.elemdescVar = typerepr, flags, defval291 doc = None292 try:293 if bForUser:294 doc = typeinfo.GetDocumentation(id)295 except pythoncom.ole_error:296 pass297 298 # handle the enumerator specially299 map = self.propMap300 # Check if the element is hidden.301 hidden = (vardesc.wVarFlags & 0x40) != 0 # VARFLAG_FHIDDEN302 map[names[0]] = MapEntry(303 vardesc, names, doc, resultCLSID, resultDoc, hidden304 )305 return (names[0], map)306 else:307 return None308 309 def Build(self, typeinfo, attr, bForUser=1):310 self.clsid = attr[0]311 self.bIsDispatch = (attr.wTypeFlags & pythoncom.TYPEFLAG_FDISPATCHABLE) != 0312 if typeinfo is None:313 return314 # Loop over all methods315 for j in range(attr[6]):316 fdesc = typeinfo.GetFuncDesc(j)317 self._AddFunc_(typeinfo, fdesc, bForUser)318 319 # Loop over all variables (ie, properties)320 for j in range(attr[7]):321 fdesc = typeinfo.GetVarDesc(j)322 self._AddVar_(typeinfo, fdesc, bForUser)323 324 # Now post-process the maps. For any "Get" or "Set" properties325 # that have arguments, we must turn them into methods. If a method326 # of the same name already exists, change the name.327 for key, item in list(self.propMapGet.items()):328 self._propMapGetCheck_(key, item)329 330 for key, item in list(self.propMapPut.items()):331 self._propMapPutCheck_(key, item)332 333 def CountInOutOptArgs(self, argTuple):334 "Return tuple counting in/outs/OPTS. Sum of result may not be len(argTuple), as some args may be in/out."335 ins = out = opts = 0336 for argCheck in argTuple:337 inOut = argCheck[1]338 if inOut == 0:339 ins = ins + 1340 out = out + 1341 else:342 if inOut & pythoncom.PARAMFLAG_FIN:343 ins = ins + 1344 if inOut & pythoncom.PARAMFLAG_FOPT:345 opts = opts + 1346 if inOut & pythoncom.PARAMFLAG_FOUT:347 out = out + 1348 return ins, out, opts349 350 def MakeFuncMethod(self, entry, name, bMakeClass=1):351 # If we have a type description, and not varargs...352 if entry.desc is not None and (len(entry.desc) < 6 or entry.desc[6] != -1):353 return self.MakeDispatchFuncMethod(entry, name, bMakeClass)354 else:355 return self.MakeVarArgsFuncMethod(entry, name, bMakeClass)356 357 def MakeDispatchFuncMethod(self, entry, name, bMakeClass=1):358 fdesc = entry.desc359 doc = entry.doc360 names = entry.names361 ret = []362 if bMakeClass:363 linePrefix = "\t"364 defNamedOptArg = "defaultNamedOptArg"365 defNamedNotOptArg = "defaultNamedNotOptArg"366 defUnnamedArg = "defaultUnnamedArg"367 else:368 linePrefix = ""369 defNamedOptArg = "pythoncom.Missing"370 defNamedNotOptArg = "pythoncom.Missing"371 defUnnamedArg = "pythoncom.Missing"372 defOutArg = "pythoncom.Missing"373 id = fdesc[0]374 375 s = (376 linePrefix377 + "def "378 + name379 + "(self"380 + BuildCallList(381 fdesc,382 names,383 defNamedOptArg,384 defNamedNotOptArg,385 defUnnamedArg,386 defOutArg,387 )388 + "):"389 )390 ret.append(s)391 if doc and doc[1]:392 ret.append(linePrefix + "\t" + _makeDocString(doc[1]))393 394 resclsid = entry.GetResultCLSID()395 if resclsid:396 resclsid = "'%s'" % resclsid397 else:398 resclsid = "None"399 # Strip the default values from the arg desc400 retDesc = fdesc[8][:2]401 argsDesc = tuple([what[:2] for what in fdesc[2]])402 # The runtime translation of the return types is expensive, so when we know the403 # return type of the function, there is no need to check the type at runtime.404 # To qualify, this function must return a "simple" type, and have no byref args.405 # Check if we have byrefs or anything in the args which mean we still need a translate.406 param_flags = [what[1] for what in fdesc[2]]407 bad_params = [408 flag409 for flag in param_flags410 if flag & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FRETVAL) != 0411 ]412 s = None413 if len(bad_params) == 0 and len(retDesc) == 2 and retDesc[1] == 0:414 rd = retDesc[0]415 if rd in NoTranslateMap:416 s = "%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)" % (417 linePrefix,418 id,419 fdesc[4],420 retDesc,421 argsDesc,422 _BuildArgList(fdesc, names),423 )424 elif rd in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN]:425 s = "%s\tret = self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)\n" % (426 linePrefix,427 id,428 fdesc[4],429 retDesc,430 repr(argsDesc),431 _BuildArgList(fdesc, names),432 )433 s = s + "%s\tif ret is not None:\n" % (linePrefix,)434 if rd == pythoncom.VT_UNKNOWN:435 s = s + "%s\t\t# See if this IUnknown is really an IDispatch\n" % (436 linePrefix,437 )438 s = s + "%s\t\ttry:\n" % (linePrefix,)439 s = (440 s441 + "%s\t\t\tret = ret.QueryInterface(pythoncom.IID_IDispatch)\n"442 % (linePrefix,)443 )444 s = s + "%s\t\texcept pythoncom.error:\n" % (linePrefix,)445 s = s + "%s\t\t\treturn ret\n" % (linePrefix,)446 s = s + "%s\t\tret = Dispatch(ret, %s, %s)\n" % (447 linePrefix,448 repr(name),449 resclsid,450 )451 s = s + "%s\treturn ret" % (linePrefix)452 elif rd == pythoncom.VT_BSTR:453 s = "%s\t# Result is a Unicode object\n" % (linePrefix,)454 s = (455 s456 + "%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)"457 % (458 linePrefix,459 id,460 fdesc[4],461 retDesc,462 repr(argsDesc),463 _BuildArgList(fdesc, names),464 )465 )466 # else s remains None467 if s is None:468 s = "%s\treturn self._ApplyTypes_(%d, %s, %s, %s, %s, %s%s)" % (469 linePrefix,470 id,471 fdesc[4],472 retDesc,473 argsDesc,474 repr(name),475 resclsid,476 _BuildArgList(fdesc, names),477 )478 479 ret.append(s)480 ret.append("")481 return ret482 483 def MakeVarArgsFuncMethod(self, entry, name, bMakeClass=1):484 fdesc = entry.desc485 names = entry.names486 doc = entry.doc487 ret = []488 argPrefix = "self"489 if bMakeClass:490 linePrefix = "\t"491 else:492 linePrefix = ""493 ret.append(linePrefix + "def " + name + "(" + argPrefix + ", *args):")494 if doc and doc[1]:495 ret.append(linePrefix + "\t" + _makeDocString(doc[1]))496 if fdesc:497 invoketype = fdesc[4]498 else:499 invoketype = pythoncom.DISPATCH_METHOD500 s = linePrefix + "\treturn self._get_good_object_(self._oleobj_.Invoke(*(("501 ret.append(502 s + str(entry.dispid) + ",0,%d,1)+args)),'%s')" % (invoketype, names[0])503 )504 ret.append("")505 return ret506 507 508# Note - "DispatchItem" poorly named - need a new intermediate class.509class VTableItem(DispatchItem):510 def Build(self, typeinfo, attr, bForUser=1):511 DispatchItem.Build(self, typeinfo, attr, bForUser)512 assert typeinfo is not None, "Cant build vtables without type info!"513 514 meth_list = (515 list(self.mapFuncs.values())516 + list(self.propMapGet.values())517 + list(self.propMapPut.values())518 )519 meth_list.sort(key=lambda m: m.desc[7])520 521 # Now turn this list into the run-time representation522 # (ready for immediate use or writing to gencache)523 self.vtableFuncs = []524 for entry in meth_list:525 self.vtableFuncs.append((entry.names, entry.dispid, entry.desc))526 527 528# A Lazy dispatch item - builds an item on request using info from529# an ITypeComp. The dynamic module makes the called to build each item,530# and also holds the references to the typeinfo and typecomp.531class LazyDispatchItem(DispatchItem):532 typename = "LazyDispatchItem"533 534 def __init__(self, attr, doc):535 self.clsid = attr[0]536 DispatchItem.__init__(self, None, attr, doc, 0)537 538 539typeSubstMap = {540 pythoncom.VT_INT: pythoncom.VT_I4,541 pythoncom.VT_UINT: pythoncom.VT_UI4,542 pythoncom.VT_HRESULT: pythoncom.VT_I4,543}544 545 546def _ResolveType(typerepr, itypeinfo):547 # Resolve VT_USERDEFINED (often aliases or typed IDispatches)548 549 if type(typerepr) == tuple:550 indir_vt, subrepr = typerepr551 if indir_vt == pythoncom.VT_PTR:552 # If it is a VT_PTR to a VT_USERDEFINED that is an IDispatch/IUnknown,553 # then it resolves to simply the object.554 # Otherwise, it becomes a ByRef of the resolved type555 # We need to drop an indirection level on pointer to user defined interfaces.556 # eg, (VT_PTR, (VT_USERDEFINED, somehandle)) needs to become VT_DISPATCH557 # only when "somehandle" is an object.558 # but (VT_PTR, (VT_USERDEFINED, otherhandle)) doesnt get the indirection dropped.559 was_user = type(subrepr) == tuple and subrepr[0] == pythoncom.VT_USERDEFINED560 subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)561 if was_user and subrepr in [562 pythoncom.VT_DISPATCH,563 pythoncom.VT_UNKNOWN,564 pythoncom.VT_RECORD,565 ]:566 # Drop the VT_PTR indirection567 return subrepr, sub_clsid, sub_doc568 # Change PTR indirection to byref569 return subrepr | pythoncom.VT_BYREF, sub_clsid, sub_doc570 if indir_vt == pythoncom.VT_SAFEARRAY:571 # resolve the array element, and convert to VT_ARRAY572 subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)573 return pythoncom.VT_ARRAY | subrepr, sub_clsid, sub_doc574 if indir_vt == pythoncom.VT_CARRAY: # runtime has no support for this yet.575 # resolve the array element, and convert to VT_CARRAY576 # sheesh - return _something_577 return pythoncom.VT_CARRAY, None, None578 if indir_vt == pythoncom.VT_USERDEFINED:579 try:580 resultTypeInfo = itypeinfo.GetRefTypeInfo(subrepr)581 except pythoncom.com_error as details:582 if details.hresult in [583 winerror.TYPE_E_CANTLOADLIBRARY,584 winerror.TYPE_E_LIBNOTREGISTERED,585 ]:586 # an unregistered interface587 return pythoncom.VT_UNKNOWN, None, None588 raise589 590 resultAttr = resultTypeInfo.GetTypeAttr()591 typeKind = resultAttr.typekind592 if typeKind == pythoncom.TKIND_ALIAS:593 tdesc = resultAttr.tdescAlias594 return _ResolveType(tdesc, resultTypeInfo)595 elif typeKind in [pythoncom.TKIND_ENUM, pythoncom.TKIND_MODULE]:596 # For now, assume Long597 return pythoncom.VT_I4, None, None598 599 elif typeKind == pythoncom.TKIND_DISPATCH:600 clsid = resultTypeInfo.GetTypeAttr()[0]601 retdoc = resultTypeInfo.GetDocumentation(-1)602 return pythoncom.VT_DISPATCH, clsid, retdoc603 604 elif typeKind in [pythoncom.TKIND_INTERFACE, pythoncom.TKIND_COCLASS]:605 # XXX - should probably get default interface for CO_CLASS???606 clsid = resultTypeInfo.GetTypeAttr()[0]607 retdoc = resultTypeInfo.GetDocumentation(-1)608 return pythoncom.VT_UNKNOWN, clsid, retdoc609 610 elif typeKind == pythoncom.TKIND_RECORD:611 return pythoncom.VT_RECORD, None, None612 raise NotSupportedException("Can not resolve alias or user-defined type")613 return typeSubstMap.get(typerepr, typerepr), None, None614 615 616def _BuildArgList(fdesc, names):617 "Builds list of args to the underlying Invoke method."618 # Word has TypeInfo for Insert() method, but says "no args"619 numArgs = max(fdesc[6], len(fdesc[2]))620 names = list(names)621 while None in names:622 i = names.index(None)623 names[i] = "arg%d" % (i,)624 # We've seen 'source safe' libraries offer the name of 'ret' params in625 # 'names' - although we can't reproduce this, it would be insane to offer626 # more args than we have arg infos for - hence the upper limit on names...627 names = list(map(MakePublicAttributeName, names[1 : (numArgs + 1)]))628 name_num = 0629 while len(names) < numArgs:630 names.append("arg%d" % (len(names),))631 # As per BuildCallList(), avoid huge lines.632 # Hack a "\n" at the end of every 5th name - "strides" would be handy633 # here but don't exist in 2.2634 for i in range(0, len(names), 5):635 names[i] = names[i] + "\n\t\t\t"636 return "," + ", ".join(names)637 638 639valid_identifier_chars = string.ascii_letters + string.digits + "_"640 641 642def demunge_leading_underscores(className):643 i = 0644 while className[i] == "_":645 i += 1646 assert i >= 2, "Should only be here with names starting with '__'"647 return className[i - 1 :] + className[: i - 1]648 649 650# Given a "public name" (eg, the name of a class, function, etc)651# make sure it is a legal (and reasonable!) Python name.652def MakePublicAttributeName(className, is_global=False):653 # Given a class attribute that needs to be public, convert it to a654 # reasonable name.655 # Also need to be careful that the munging doesnt656 # create duplicates - eg, just removing a leading "_" is likely to cause657 # a clash.658 # if is_global is True, then the name is a global variable that may659 # overwrite a builtin - eg, "None"660 if className[:2] == "__":661 return demunge_leading_underscores(className)662 elif className == "None":663 # assign to None is evil (and SyntaxError in 2.4, even though664 # iskeyword says False there) - note that if it was a global665 # it would get picked up below666 className = "NONE"667 elif iskeyword(className):668 # most keywords are lower case (except True, False etc in py3k)669 ret = className.capitalize()670 # but those which aren't get forced upper.671 if ret == className:672 ret = ret.upper()673 return ret674 elif is_global and hasattr(__builtins__, className):675 # builtins may be mixed case. If capitalizing it doesn't change it,676 # force to all uppercase (eg, "None", "True" become "NONE", "TRUE"677 ret = className.capitalize()678 if ret == className: # didn't change - force all uppercase.679 ret = ret.upper()680 return ret681 # Strip non printable chars682 return "".join([char for char in className if char in valid_identifier_chars])683 684 685# Given a default value passed by a type library, return a string with686# an appropriate repr() for the type.687# Takes a raw ELEMDESC and returns a repr string, or None688# (NOTE: The string itself may be '"None"', which is valid, and different to None.689# XXX - To do: Dates are probably screwed, but can they come in?690def MakeDefaultArgRepr(defArgVal):691 try:692 inOut = defArgVal[1]693 except IndexError:694 # something strange - assume is in param.695 inOut = pythoncom.PARAMFLAG_FIN696 697 if inOut & pythoncom.PARAMFLAG_FHASDEFAULT:698 # times need special handling...699 val = defArgVal[2]700 if isinstance(val, datetime.datetime):701 # VARIANT <-> SYSTEMTIME conversions always lose any sub-second702 # resolution, so just use a 'timetuple' here.703 return repr(tuple(val.utctimetuple()))704 if type(val) is TimeType:705 # must be the 'old' pywintypes time object...706 year = val.year707 month = val.month708 day = val.day709 hour = val.hour710 minute = val.minute711 second = val.second712 msec = val.msec713 return (714 "pywintypes.Time((%(year)d, %(month)d, %(day)d, %(hour)d, %(minute)d, %(second)d,0,0,0,%(msec)d))"715 % locals()716 )717 return repr(val)718 return None719 720 721def BuildCallList(722 fdesc,723 names,724 defNamedOptArg,725 defNamedNotOptArg,726 defUnnamedArg,727 defOutArg,728 is_comment=False,729):730 "Builds a Python declaration for a method."731 # Names[0] is the func name - param names are from 1.732 numArgs = len(fdesc[2])733 numOptArgs = fdesc[6]734 strval = ""735 if numOptArgs == -1: # Special value that says "var args after here"736 firstOptArg = numArgs737 numArgs = numArgs - 1738 else:739 firstOptArg = numArgs - numOptArgs740 for arg in range(numArgs):741 try:742 argName = names[arg + 1]743 namedArg = argName is not None744 except IndexError:745 namedArg = 0746 if not namedArg:747 argName = "arg%d" % (arg)748 thisdesc = fdesc[2][arg]749 # See if the IDL specified a default value750 defArgVal = MakeDefaultArgRepr(thisdesc)751 if defArgVal is None:752 # Out params always get their special default753 if (754 thisdesc[1] & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FIN)755 == pythoncom.PARAMFLAG_FOUT756 ):757 defArgVal = defOutArg758 else:759 # Unnamed arg - always allow default values.760 if namedArg:761 # Is a named argument762 if arg >= firstOptArg:763 defArgVal = defNamedOptArg764 else:765 defArgVal = defNamedNotOptArg766 else:767 defArgVal = defUnnamedArg768 769 argName = MakePublicAttributeName(argName)770 # insanely long lines with an 'encoding' flag crashes python 2.4.0771 # keep 5 args per line772 # This may still fail if the arg names are insane, but that seems773 # unlikely. See also _BuildArgList()774 if (arg + 1) % 5 == 0:775 strval = strval + "\n"776 if is_comment:777 strval = strval + "#"778 strval = strval + "\t\t\t"779 strval = strval + ", " + argName780 if defArgVal:781 strval = strval + "=" + defArgVal782 if numOptArgs == -1:783 strval = strval + ", *" + names[-1]784 785 return strval786 787 788if __name__ == "__main__":789 print("Use 'makepy.py' to generate Python code - this module is just a helper")790 