Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

build.py 28KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. """Contains knowledge to build a COM object definition.
  2. This module is used by both the @dynamic@ and @makepy@ modules to build
  3. all knowledge of a COM object.
  4. This module contains classes which contain the actual knowledge of the object.
  5. This include parameter and return type information, the COM dispid and CLSID, etc.
  6. Other modules may use this information to generate .py files, use the information
  7. dynamically, or possibly even generate .html documentation for objects.
  8. """
  9. #
  10. # NOTES: DispatchItem and MapEntry used by dynamic.py.
  11. # the rest is used by makepy.py
  12. #
  13. # OleItem, DispatchItem, MapEntry, BuildCallList() is used by makepy
  14. import datetime
  15. import string
  16. import sys
  17. from keyword import iskeyword
  18. import pythoncom
  19. import winerror
  20. from pywintypes import TimeType
  21. # It isn't really clear what the quoting rules are in a C/IDL string and
  22. # literals like a quote char and backslashes makes life a little painful to
  23. # always render the string perfectly - so just punt and fall-back to a repr()
  24. def _makeDocString(s):
  25. if sys.version_info < (3,):
  26. s = s.encode("mbcs")
  27. return repr(s)
  28. error = "PythonCOM.Client.Build error"
  29. class NotSupportedException(Exception):
  30. pass # Raised when we cant support a param type.
  31. DropIndirection = "DropIndirection"
  32. NoTranslateTypes = [
  33. pythoncom.VT_BOOL,
  34. pythoncom.VT_CLSID,
  35. pythoncom.VT_CY,
  36. pythoncom.VT_DATE,
  37. pythoncom.VT_DECIMAL,
  38. pythoncom.VT_EMPTY,
  39. pythoncom.VT_ERROR,
  40. pythoncom.VT_FILETIME,
  41. pythoncom.VT_HRESULT,
  42. pythoncom.VT_I1,
  43. pythoncom.VT_I2,
  44. pythoncom.VT_I4,
  45. pythoncom.VT_I8,
  46. pythoncom.VT_INT,
  47. pythoncom.VT_NULL,
  48. pythoncom.VT_R4,
  49. pythoncom.VT_R8,
  50. pythoncom.VT_NULL,
  51. pythoncom.VT_STREAM,
  52. pythoncom.VT_UI1,
  53. pythoncom.VT_UI2,
  54. pythoncom.VT_UI4,
  55. pythoncom.VT_UI8,
  56. pythoncom.VT_UINT,
  57. pythoncom.VT_VOID,
  58. ]
  59. NoTranslateMap = {}
  60. for v in NoTranslateTypes:
  61. NoTranslateMap[v] = None
  62. class MapEntry:
  63. "Simple holder for named attibutes - items in a map."
  64. def __init__(
  65. self,
  66. desc_or_id,
  67. names=None,
  68. doc=None,
  69. resultCLSID=pythoncom.IID_NULL,
  70. resultDoc=None,
  71. hidden=0,
  72. ):
  73. if type(desc_or_id) == type(0):
  74. self.dispid = desc_or_id
  75. self.desc = None
  76. else:
  77. self.dispid = desc_or_id[0]
  78. self.desc = desc_or_id
  79. self.names = names
  80. self.doc = doc
  81. self.resultCLSID = resultCLSID
  82. self.resultDocumentation = resultDoc
  83. self.wasProperty = (
  84. 0 # Have I been transformed into a function so I can pass args?
  85. )
  86. self.hidden = hidden
  87. def __repr__(self):
  88. return (
  89. "MapEntry(dispid={s.dispid}, desc={s.desc}, names={s.names}, doc={s.doc!r}, "
  90. "resultCLSID={s.resultCLSID}, resultDocumentation={s.resultDocumentation}, "
  91. "wasProperty={s.wasProperty}, hidden={s.hidden}"
  92. ).format(s=self)
  93. def GetResultCLSID(self):
  94. rc = self.resultCLSID
  95. if rc == pythoncom.IID_NULL:
  96. return None
  97. return rc
  98. # Return a string, suitable for output - either "'{...}'" or "None"
  99. def GetResultCLSIDStr(self):
  100. rc = self.GetResultCLSID()
  101. if rc is None:
  102. return "None"
  103. return repr(
  104. str(rc)
  105. ) # Convert the IID object to a string, then to a string in a string.
  106. def GetResultName(self):
  107. if self.resultDocumentation is None:
  108. return None
  109. return self.resultDocumentation[0]
  110. class OleItem:
  111. typename = "OleItem"
  112. def __init__(self, doc=None):
  113. self.doc = doc
  114. if self.doc:
  115. self.python_name = MakePublicAttributeName(self.doc[0])
  116. else:
  117. self.python_name = None
  118. self.bWritten = 0
  119. self.bIsDispatch = 0
  120. self.bIsSink = 0
  121. self.clsid = None
  122. self.co_class = None
  123. class DispatchItem(OleItem):
  124. typename = "DispatchItem"
  125. def __init__(self, typeinfo=None, attr=None, doc=None, bForUser=1):
  126. OleItem.__init__(self, doc)
  127. self.propMap = {}
  128. self.propMapGet = {}
  129. self.propMapPut = {}
  130. self.mapFuncs = {}
  131. self.defaultDispatchName = None
  132. self.hidden = 0
  133. if typeinfo:
  134. self.Build(typeinfo, attr, bForUser)
  135. def _propMapPutCheck_(self, key, item):
  136. ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
  137. if ins > 1: # if a Put property takes more than 1 arg:
  138. if opts + 1 == ins or ins == item.desc[6] + 1:
  139. newKey = "Set" + key
  140. deleteExisting = 0 # This one is still OK
  141. else:
  142. deleteExisting = 1 # No good to us
  143. if key in self.mapFuncs or key in self.propMapGet:
  144. newKey = "Set" + key
  145. else:
  146. newKey = key
  147. item.wasProperty = 1
  148. self.mapFuncs[newKey] = item
  149. if deleteExisting:
  150. del self.propMapPut[key]
  151. def _propMapGetCheck_(self, key, item):
  152. ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
  153. if ins > 0: # if a Get property takes _any_ in args:
  154. if item.desc[6] == ins or ins == opts:
  155. newKey = "Get" + key
  156. deleteExisting = 0 # This one is still OK
  157. else:
  158. deleteExisting = 1 # No good to us
  159. if key in self.mapFuncs:
  160. newKey = "Get" + key
  161. else:
  162. newKey = key
  163. item.wasProperty = 1
  164. self.mapFuncs[newKey] = item
  165. if deleteExisting:
  166. del self.propMapGet[key]
  167. def _AddFunc_(self, typeinfo, fdesc, bForUser):
  168. assert fdesc.desckind == pythoncom.DESCKIND_FUNCDESC
  169. id = fdesc.memid
  170. funcflags = fdesc.wFuncFlags
  171. try:
  172. names = typeinfo.GetNames(id)
  173. name = names[0]
  174. except pythoncom.ole_error:
  175. name = ""
  176. names = None
  177. doc = None
  178. try:
  179. if bForUser:
  180. doc = typeinfo.GetDocumentation(id)
  181. except pythoncom.ole_error:
  182. pass
  183. if id == 0 and name:
  184. self.defaultDispatchName = name
  185. invkind = fdesc.invkind
  186. # We need to translate any Alias', Enums, structs etc in result and args
  187. typerepr, flag, defval = fdesc.rettype
  188. # sys.stderr.write("%s result - %s -> " % (name, typerepr))
  189. typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
  190. # sys.stderr.write("%s\n" % (typerepr,))
  191. fdesc.rettype = typerepr, flag, defval, resultCLSID
  192. # Translate any Alias or Enums in argument list.
  193. argList = []
  194. for argDesc in fdesc.args:
  195. typerepr, flag, defval = argDesc
  196. # sys.stderr.write("%s arg - %s -> " % (name, typerepr))
  197. arg_type, arg_clsid, arg_doc = _ResolveType(typerepr, typeinfo)
  198. argDesc = arg_type, flag, defval, arg_clsid
  199. # sys.stderr.write("%s\n" % (argDesc[0],))
  200. argList.append(argDesc)
  201. fdesc.args = tuple(argList)
  202. hidden = (funcflags & pythoncom.FUNCFLAG_FHIDDEN) != 0
  203. if invkind == pythoncom.INVOKE_PROPERTYGET:
  204. map = self.propMapGet
  205. # This is not the best solution, but I dont think there is
  206. # one without specific "set" syntax.
  207. # If there is a single PUT or PUTREF, it will function as a property.
  208. # If there are both, then the PUT remains a property, and the PUTREF
  209. # gets transformed into a function.
  210. # (in vb, PUT=="obj=other_obj", PUTREF="set obj=other_obj
  211. elif invkind in (pythoncom.INVOKE_PROPERTYPUT, pythoncom.INVOKE_PROPERTYPUTREF):
  212. # Special case
  213. existing = self.propMapPut.get(name, None)
  214. if existing is not None:
  215. if existing.desc[4] == pythoncom.INVOKE_PROPERTYPUT: # Keep this one
  216. map = self.mapFuncs
  217. name = "Set" + name
  218. else: # Existing becomes a func.
  219. existing.wasProperty = 1
  220. self.mapFuncs["Set" + name] = existing
  221. map = self.propMapPut # existing gets overwritten below.
  222. else:
  223. map = self.propMapPut # first time weve seen it.
  224. elif invkind == pythoncom.INVOKE_FUNC:
  225. map = self.mapFuncs
  226. else:
  227. map = None
  228. if not map is None:
  229. # if map.has_key(name):
  230. # sys.stderr.write("Warning - overwriting existing method/attribute %s\n" % name)
  231. map[name] = MapEntry(fdesc, names, doc, resultCLSID, resultDoc, hidden)
  232. # any methods that can't be reached via DISPATCH we return None
  233. # for, so dynamic dispatch doesnt see it.
  234. if fdesc.funckind != pythoncom.FUNC_DISPATCH:
  235. return None
  236. return (name, map)
  237. return None
  238. def _AddVar_(self, typeinfo, vardesc, bForUser):
  239. ### need pythoncom.VARFLAG_FRESTRICTED ...
  240. ### then check it
  241. assert vardesc.desckind == pythoncom.DESCKIND_VARDESC
  242. if vardesc.varkind == pythoncom.VAR_DISPATCH:
  243. id = vardesc.memid
  244. names = typeinfo.GetNames(id)
  245. # Translate any Alias or Enums in result.
  246. typerepr, flags, defval = vardesc.elemdescVar
  247. typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
  248. vardesc.elemdescVar = typerepr, flags, defval
  249. doc = None
  250. try:
  251. if bForUser:
  252. doc = typeinfo.GetDocumentation(id)
  253. except pythoncom.ole_error:
  254. pass
  255. # handle the enumerator specially
  256. map = self.propMap
  257. # Check if the element is hidden.
  258. hidden = (vardesc.wVarFlags & 0x40) != 0 # VARFLAG_FHIDDEN
  259. map[names[0]] = MapEntry(
  260. vardesc, names, doc, resultCLSID, resultDoc, hidden
  261. )
  262. return (names[0], map)
  263. else:
  264. return None
  265. def Build(self, typeinfo, attr, bForUser=1):
  266. self.clsid = attr[0]
  267. self.bIsDispatch = (attr.wTypeFlags & pythoncom.TYPEFLAG_FDISPATCHABLE) != 0
  268. if typeinfo is None:
  269. return
  270. # Loop over all methods
  271. for j in range(attr[6]):
  272. fdesc = typeinfo.GetFuncDesc(j)
  273. self._AddFunc_(typeinfo, fdesc, bForUser)
  274. # Loop over all variables (ie, properties)
  275. for j in range(attr[7]):
  276. fdesc = typeinfo.GetVarDesc(j)
  277. self._AddVar_(typeinfo, fdesc, bForUser)
  278. # Now post-process the maps. For any "Get" or "Set" properties
  279. # that have arguments, we must turn them into methods. If a method
  280. # of the same name already exists, change the name.
  281. for key, item in list(self.propMapGet.items()):
  282. self._propMapGetCheck_(key, item)
  283. for key, item in list(self.propMapPut.items()):
  284. self._propMapPutCheck_(key, item)
  285. def CountInOutOptArgs(self, argTuple):
  286. "Return tuple counting in/outs/OPTS. Sum of result may not be len(argTuple), as some args may be in/out."
  287. ins = out = opts = 0
  288. for argCheck in argTuple:
  289. inOut = argCheck[1]
  290. if inOut == 0:
  291. ins = ins + 1
  292. out = out + 1
  293. else:
  294. if inOut & pythoncom.PARAMFLAG_FIN:
  295. ins = ins + 1
  296. if inOut & pythoncom.PARAMFLAG_FOPT:
  297. opts = opts + 1
  298. if inOut & pythoncom.PARAMFLAG_FOUT:
  299. out = out + 1
  300. return ins, out, opts
  301. def MakeFuncMethod(self, entry, name, bMakeClass=1):
  302. # If we have a type description, and not varargs...
  303. if entry.desc is not None and (len(entry.desc) < 6 or entry.desc[6] != -1):
  304. return self.MakeDispatchFuncMethod(entry, name, bMakeClass)
  305. else:
  306. return self.MakeVarArgsFuncMethod(entry, name, bMakeClass)
  307. def MakeDispatchFuncMethod(self, entry, name, bMakeClass=1):
  308. fdesc = entry.desc
  309. doc = entry.doc
  310. names = entry.names
  311. ret = []
  312. if bMakeClass:
  313. linePrefix = "\t"
  314. defNamedOptArg = "defaultNamedOptArg"
  315. defNamedNotOptArg = "defaultNamedNotOptArg"
  316. defUnnamedArg = "defaultUnnamedArg"
  317. else:
  318. linePrefix = ""
  319. defNamedOptArg = "pythoncom.Missing"
  320. defNamedNotOptArg = "pythoncom.Missing"
  321. defUnnamedArg = "pythoncom.Missing"
  322. defOutArg = "pythoncom.Missing"
  323. id = fdesc[0]
  324. s = (
  325. linePrefix
  326. + "def "
  327. + name
  328. + "(self"
  329. + BuildCallList(
  330. fdesc,
  331. names,
  332. defNamedOptArg,
  333. defNamedNotOptArg,
  334. defUnnamedArg,
  335. defOutArg,
  336. )
  337. + "):"
  338. )
  339. ret.append(s)
  340. if doc and doc[1]:
  341. ret.append(linePrefix + "\t" + _makeDocString(doc[1]))
  342. resclsid = entry.GetResultCLSID()
  343. if resclsid:
  344. resclsid = "'%s'" % resclsid
  345. else:
  346. resclsid = "None"
  347. # Strip the default values from the arg desc
  348. retDesc = fdesc[8][:2]
  349. argsDesc = tuple([what[:2] for what in fdesc[2]])
  350. # The runtime translation of the return types is expensive, so when we know the
  351. # return type of the function, there is no need to check the type at runtime.
  352. # To qualify, this function must return a "simple" type, and have no byref args.
  353. # Check if we have byrefs or anything in the args which mean we still need a translate.
  354. param_flags = [what[1] for what in fdesc[2]]
  355. bad_params = [
  356. flag
  357. for flag in param_flags
  358. if flag & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FRETVAL) != 0
  359. ]
  360. s = None
  361. if len(bad_params) == 0 and len(retDesc) == 2 and retDesc[1] == 0:
  362. rd = retDesc[0]
  363. if rd in NoTranslateMap:
  364. s = "%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)" % (
  365. linePrefix,
  366. id,
  367. fdesc[4],
  368. retDesc,
  369. argsDesc,
  370. _BuildArgList(fdesc, names),
  371. )
  372. elif rd in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN]:
  373. s = "%s\tret = self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)\n" % (
  374. linePrefix,
  375. id,
  376. fdesc[4],
  377. retDesc,
  378. repr(argsDesc),
  379. _BuildArgList(fdesc, names),
  380. )
  381. s = s + "%s\tif ret is not None:\n" % (linePrefix,)
  382. if rd == pythoncom.VT_UNKNOWN:
  383. s = s + "%s\t\t# See if this IUnknown is really an IDispatch\n" % (
  384. linePrefix,
  385. )
  386. s = s + "%s\t\ttry:\n" % (linePrefix,)
  387. s = (
  388. s
  389. + "%s\t\t\tret = ret.QueryInterface(pythoncom.IID_IDispatch)\n"
  390. % (linePrefix,)
  391. )
  392. s = s + "%s\t\texcept pythoncom.error:\n" % (linePrefix,)
  393. s = s + "%s\t\t\treturn ret\n" % (linePrefix,)
  394. s = s + "%s\t\tret = Dispatch(ret, %s, %s)\n" % (
  395. linePrefix,
  396. repr(name),
  397. resclsid,
  398. )
  399. s = s + "%s\treturn ret" % (linePrefix)
  400. elif rd == pythoncom.VT_BSTR:
  401. s = "%s\t# Result is a Unicode object\n" % (linePrefix,)
  402. s = (
  403. s
  404. + "%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)"
  405. % (
  406. linePrefix,
  407. id,
  408. fdesc[4],
  409. retDesc,
  410. repr(argsDesc),
  411. _BuildArgList(fdesc, names),
  412. )
  413. )
  414. # else s remains None
  415. if s is None:
  416. s = "%s\treturn self._ApplyTypes_(%d, %s, %s, %s, %s, %s%s)" % (
  417. linePrefix,
  418. id,
  419. fdesc[4],
  420. retDesc,
  421. argsDesc,
  422. repr(name),
  423. resclsid,
  424. _BuildArgList(fdesc, names),
  425. )
  426. ret.append(s)
  427. ret.append("")
  428. return ret
  429. def MakeVarArgsFuncMethod(self, entry, name, bMakeClass=1):
  430. fdesc = entry.desc
  431. names = entry.names
  432. doc = entry.doc
  433. ret = []
  434. argPrefix = "self"
  435. if bMakeClass:
  436. linePrefix = "\t"
  437. else:
  438. linePrefix = ""
  439. ret.append(linePrefix + "def " + name + "(" + argPrefix + ", *args):")
  440. if doc and doc[1]:
  441. ret.append(linePrefix + "\t" + _makeDocString(doc[1]))
  442. if fdesc:
  443. invoketype = fdesc[4]
  444. else:
  445. invoketype = pythoncom.DISPATCH_METHOD
  446. s = linePrefix + "\treturn self._get_good_object_(self._oleobj_.Invoke(*(("
  447. ret.append(
  448. s + str(entry.dispid) + ",0,%d,1)+args)),'%s')" % (invoketype, names[0])
  449. )
  450. ret.append("")
  451. return ret
  452. # Note - "DispatchItem" poorly named - need a new intermediate class.
  453. class VTableItem(DispatchItem):
  454. def Build(self, typeinfo, attr, bForUser=1):
  455. DispatchItem.Build(self, typeinfo, attr, bForUser)
  456. assert typeinfo is not None, "Cant build vtables without type info!"
  457. meth_list = (
  458. list(self.mapFuncs.values())
  459. + list(self.propMapGet.values())
  460. + list(self.propMapPut.values())
  461. )
  462. meth_list.sort(key=lambda m: m.desc[7])
  463. # Now turn this list into the run-time representation
  464. # (ready for immediate use or writing to gencache)
  465. self.vtableFuncs = []
  466. for entry in meth_list:
  467. self.vtableFuncs.append((entry.names, entry.dispid, entry.desc))
  468. # A Lazy dispatch item - builds an item on request using info from
  469. # an ITypeComp. The dynamic module makes the called to build each item,
  470. # and also holds the references to the typeinfo and typecomp.
  471. class LazyDispatchItem(DispatchItem):
  472. typename = "LazyDispatchItem"
  473. def __init__(self, attr, doc):
  474. self.clsid = attr[0]
  475. DispatchItem.__init__(self, None, attr, doc, 0)
  476. typeSubstMap = {
  477. pythoncom.VT_INT: pythoncom.VT_I4,
  478. pythoncom.VT_UINT: pythoncom.VT_UI4,
  479. pythoncom.VT_HRESULT: pythoncom.VT_I4,
  480. }
  481. def _ResolveType(typerepr, itypeinfo):
  482. # Resolve VT_USERDEFINED (often aliases or typed IDispatches)
  483. if type(typerepr) == tuple:
  484. indir_vt, subrepr = typerepr
  485. if indir_vt == pythoncom.VT_PTR:
  486. # If it is a VT_PTR to a VT_USERDEFINED that is an IDispatch/IUnknown,
  487. # then it resolves to simply the object.
  488. # Otherwise, it becomes a ByRef of the resolved type
  489. # We need to drop an indirection level on pointer to user defined interfaces.
  490. # eg, (VT_PTR, (VT_USERDEFINED, somehandle)) needs to become VT_DISPATCH
  491. # only when "somehandle" is an object.
  492. # but (VT_PTR, (VT_USERDEFINED, otherhandle)) doesnt get the indirection dropped.
  493. was_user = type(subrepr) == tuple and subrepr[0] == pythoncom.VT_USERDEFINED
  494. subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
  495. if was_user and subrepr in [
  496. pythoncom.VT_DISPATCH,
  497. pythoncom.VT_UNKNOWN,
  498. pythoncom.VT_RECORD,
  499. ]:
  500. # Drop the VT_PTR indirection
  501. return subrepr, sub_clsid, sub_doc
  502. # Change PTR indirection to byref
  503. return subrepr | pythoncom.VT_BYREF, sub_clsid, sub_doc
  504. if indir_vt == pythoncom.VT_SAFEARRAY:
  505. # resolve the array element, and convert to VT_ARRAY
  506. subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
  507. return pythoncom.VT_ARRAY | subrepr, sub_clsid, sub_doc
  508. if indir_vt == pythoncom.VT_CARRAY: # runtime has no support for this yet.
  509. # resolve the array element, and convert to VT_CARRAY
  510. # sheesh - return _something_
  511. return pythoncom.VT_CARRAY, None, None
  512. if indir_vt == pythoncom.VT_USERDEFINED:
  513. try:
  514. resultTypeInfo = itypeinfo.GetRefTypeInfo(subrepr)
  515. except pythoncom.com_error as details:
  516. if details.hresult in [
  517. winerror.TYPE_E_CANTLOADLIBRARY,
  518. winerror.TYPE_E_LIBNOTREGISTERED,
  519. ]:
  520. # an unregistered interface
  521. return pythoncom.VT_UNKNOWN, None, None
  522. raise
  523. resultAttr = resultTypeInfo.GetTypeAttr()
  524. typeKind = resultAttr.typekind
  525. if typeKind == pythoncom.TKIND_ALIAS:
  526. tdesc = resultAttr.tdescAlias
  527. return _ResolveType(tdesc, resultTypeInfo)
  528. elif typeKind in [pythoncom.TKIND_ENUM, pythoncom.TKIND_MODULE]:
  529. # For now, assume Long
  530. return pythoncom.VT_I4, None, None
  531. elif typeKind == pythoncom.TKIND_DISPATCH:
  532. clsid = resultTypeInfo.GetTypeAttr()[0]
  533. retdoc = resultTypeInfo.GetDocumentation(-1)
  534. return pythoncom.VT_DISPATCH, clsid, retdoc
  535. elif typeKind in [pythoncom.TKIND_INTERFACE, pythoncom.TKIND_COCLASS]:
  536. # XXX - should probably get default interface for CO_CLASS???
  537. clsid = resultTypeInfo.GetTypeAttr()[0]
  538. retdoc = resultTypeInfo.GetDocumentation(-1)
  539. return pythoncom.VT_UNKNOWN, clsid, retdoc
  540. elif typeKind == pythoncom.TKIND_RECORD:
  541. return pythoncom.VT_RECORD, None, None
  542. raise NotSupportedException("Can not resolve alias or user-defined type")
  543. return typeSubstMap.get(typerepr, typerepr), None, None
  544. def _BuildArgList(fdesc, names):
  545. "Builds list of args to the underlying Invoke method."
  546. # Word has TypeInfo for Insert() method, but says "no args"
  547. numArgs = max(fdesc[6], len(fdesc[2]))
  548. names = list(names)
  549. while None in names:
  550. i = names.index(None)
  551. names[i] = "arg%d" % (i,)
  552. # We've seen 'source safe' libraries offer the name of 'ret' params in
  553. # 'names' - although we can't reproduce this, it would be insane to offer
  554. # more args than we have arg infos for - hence the upper limit on names...
  555. names = list(map(MakePublicAttributeName, names[1 : (numArgs + 1)]))
  556. name_num = 0
  557. while len(names) < numArgs:
  558. names.append("arg%d" % (len(names),))
  559. # As per BuildCallList(), avoid huge lines.
  560. # Hack a "\n" at the end of every 5th name - "strides" would be handy
  561. # here but don't exist in 2.2
  562. for i in range(0, len(names), 5):
  563. names[i] = names[i] + "\n\t\t\t"
  564. return "," + ", ".join(names)
  565. valid_identifier_chars = string.ascii_letters + string.digits + "_"
  566. def demunge_leading_underscores(className):
  567. i = 0
  568. while className[i] == "_":
  569. i += 1
  570. assert i >= 2, "Should only be here with names starting with '__'"
  571. return className[i - 1 :] + className[: i - 1]
  572. # Given a "public name" (eg, the name of a class, function, etc)
  573. # make sure it is a legal (and reasonable!) Python name.
  574. def MakePublicAttributeName(className, is_global=False):
  575. # Given a class attribute that needs to be public, convert it to a
  576. # reasonable name.
  577. # Also need to be careful that the munging doesnt
  578. # create duplicates - eg, just removing a leading "_" is likely to cause
  579. # a clash.
  580. # if is_global is True, then the name is a global variable that may
  581. # overwrite a builtin - eg, "None"
  582. if className[:2] == "__":
  583. return demunge_leading_underscores(className)
  584. elif className == "None":
  585. # assign to None is evil (and SyntaxError in 2.4, even though
  586. # iskeyword says False there) - note that if it was a global
  587. # it would get picked up below
  588. className = "NONE"
  589. elif iskeyword(className):
  590. # most keywords are lower case (except True, False etc in py3k)
  591. ret = className.capitalize()
  592. # but those which aren't get forced upper.
  593. if ret == className:
  594. ret = ret.upper()
  595. return ret
  596. elif is_global and hasattr(__builtins__, className):
  597. # builtins may be mixed case. If capitalizing it doesn't change it,
  598. # force to all uppercase (eg, "None", "True" become "NONE", "TRUE"
  599. ret = className.capitalize()
  600. if ret == className: # didn't change - force all uppercase.
  601. ret = ret.upper()
  602. return ret
  603. # Strip non printable chars
  604. return "".join([char for char in className if char in valid_identifier_chars])
  605. # Given a default value passed by a type library, return a string with
  606. # an appropriate repr() for the type.
  607. # Takes a raw ELEMDESC and returns a repr string, or None
  608. # (NOTE: The string itself may be '"None"', which is valid, and different to None.
  609. # XXX - To do: Dates are probably screwed, but can they come in?
  610. def MakeDefaultArgRepr(defArgVal):
  611. try:
  612. inOut = defArgVal[1]
  613. except IndexError:
  614. # something strange - assume is in param.
  615. inOut = pythoncom.PARAMFLAG_FIN
  616. if inOut & pythoncom.PARAMFLAG_FHASDEFAULT:
  617. # times need special handling...
  618. val = defArgVal[2]
  619. if isinstance(val, datetime.datetime):
  620. # VARIANT <-> SYSTEMTIME conversions always lose any sub-second
  621. # resolution, so just use a 'timetuple' here.
  622. return repr(tuple(val.utctimetuple()))
  623. if type(val) is TimeType:
  624. # must be the 'old' pywintypes time object...
  625. year = val.year
  626. month = val.month
  627. day = val.day
  628. hour = val.hour
  629. minute = val.minute
  630. second = val.second
  631. msec = val.msec
  632. return (
  633. "pywintypes.Time((%(year)d, %(month)d, %(day)d, %(hour)d, %(minute)d, %(second)d,0,0,0,%(msec)d))"
  634. % locals()
  635. )
  636. return repr(val)
  637. return None
  638. def BuildCallList(
  639. fdesc,
  640. names,
  641. defNamedOptArg,
  642. defNamedNotOptArg,
  643. defUnnamedArg,
  644. defOutArg,
  645. is_comment=False,
  646. ):
  647. "Builds a Python declaration for a method."
  648. # Names[0] is the func name - param names are from 1.
  649. numArgs = len(fdesc[2])
  650. numOptArgs = fdesc[6]
  651. strval = ""
  652. if numOptArgs == -1: # Special value that says "var args after here"
  653. firstOptArg = numArgs
  654. numArgs = numArgs - 1
  655. else:
  656. firstOptArg = numArgs - numOptArgs
  657. for arg in range(numArgs):
  658. try:
  659. argName = names[arg + 1]
  660. namedArg = argName is not None
  661. except IndexError:
  662. namedArg = 0
  663. if not namedArg:
  664. argName = "arg%d" % (arg)
  665. thisdesc = fdesc[2][arg]
  666. # See if the IDL specified a default value
  667. defArgVal = MakeDefaultArgRepr(thisdesc)
  668. if defArgVal is None:
  669. # Out params always get their special default
  670. if (
  671. thisdesc[1] & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FIN)
  672. == pythoncom.PARAMFLAG_FOUT
  673. ):
  674. defArgVal = defOutArg
  675. else:
  676. # Unnamed arg - always allow default values.
  677. if namedArg:
  678. # Is a named argument
  679. if arg >= firstOptArg:
  680. defArgVal = defNamedOptArg
  681. else:
  682. defArgVal = defNamedNotOptArg
  683. else:
  684. defArgVal = defUnnamedArg
  685. argName = MakePublicAttributeName(argName)
  686. # insanely long lines with an 'encoding' flag crashes python 2.4.0
  687. # keep 5 args per line
  688. # This may still fail if the arg names are insane, but that seems
  689. # unlikely. See also _BuildArgList()
  690. if (arg + 1) % 5 == 0:
  691. strval = strval + "\n"
  692. if is_comment:
  693. strval = strval + "#"
  694. strval = strval + "\t\t\t"
  695. strval = strval + ", " + argName
  696. if defArgVal:
  697. strval = strval + "=" + defArgVal
  698. if numOptArgs == -1:
  699. strval = strval + ", *" + names[-1]
  700. return strval
  701. if __name__ == "__main__":
  702. print("Use 'makepy.py' to generate Python code - this module is just a helper")