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.

makepy.py 15KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. # Originally written by Curt Hagenlocher, and various bits
  2. # and pieces by Mark Hammond (and now Greg Stein has had
  3. # a go too :-)
  4. # Note that the main worker code has been moved to genpy.py
  5. # As this is normally run from the command line, it reparses the code each time.
  6. # Now this is nothing more than the command line handler and public interface.
  7. # XXX - TO DO
  8. # XXX - Greg and Mark have some ideas for a revamp - just no
  9. # time - if you want to help, contact us for details.
  10. # Main idea is to drop the classes exported and move to a more
  11. # traditional data driven model.
  12. """Generate a .py file from an OLE TypeLibrary file.
  13. This module is concerned only with the actual writing of
  14. a .py file. It draws on the @build@ module, which builds
  15. the knowledge of a COM interface.
  16. """
  17. usageHelp = """ \
  18. Usage:
  19. makepy.py [-i] [-v|q] [-h] [-u] [-o output_file] [-d] [typelib, ...]
  20. -i -- Show information for the specified typelib.
  21. -v -- Verbose output.
  22. -q -- Quiet output.
  23. -h -- Do not generate hidden methods.
  24. -u -- Python 1.5 and earlier: Do NOT convert all Unicode objects to
  25. strings.
  26. Python 1.6 and later: Convert all Unicode objects to strings.
  27. -o -- Create output in a specified output file. If the path leading
  28. to the file does not exist, any missing directories will be
  29. created.
  30. NOTE: -o cannot be used with -d. This will generate an error.
  31. -d -- Generate the base code now and the class code on demand.
  32. Recommended for large type libraries.
  33. typelib -- A TLB, DLL, OCX or anything containing COM type information.
  34. If a typelib is not specified, a window containing a textbox
  35. will open from which you can select a registered type
  36. library.
  37. Examples:
  38. makepy.py -d
  39. Presents a list of registered type libraries from which you can make
  40. a selection.
  41. makepy.py -d "Microsoft Excel 8.0 Object Library"
  42. Generate support for the type library with the specified description
  43. (in this case, the MS Excel object model).
  44. """
  45. import importlib
  46. import os
  47. import sys
  48. import pythoncom
  49. from win32com.client import Dispatch, gencache, genpy, selecttlb
  50. bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also gencache.py
  51. error = "makepy.error"
  52. def usage():
  53. sys.stderr.write(usageHelp)
  54. sys.exit(2)
  55. def ShowInfo(spec):
  56. if not spec:
  57. tlbSpec = selecttlb.SelectTlb(excludeFlags=selecttlb.FLAG_HIDDEN)
  58. if tlbSpec is None:
  59. return
  60. try:
  61. tlb = pythoncom.LoadRegTypeLib(
  62. tlbSpec.clsid, tlbSpec.major, tlbSpec.minor, tlbSpec.lcid
  63. )
  64. except pythoncom.com_error: # May be badly registered.
  65. sys.stderr.write(
  66. "Warning - could not load registered typelib '%s'\n" % (tlbSpec.clsid)
  67. )
  68. tlb = None
  69. infos = [(tlb, tlbSpec)]
  70. else:
  71. infos = GetTypeLibsForSpec(spec)
  72. for tlb, tlbSpec in infos:
  73. desc = tlbSpec.desc
  74. if desc is None:
  75. if tlb is None:
  76. desc = "<Could not load typelib %s>" % (tlbSpec.dll)
  77. else:
  78. desc = tlb.GetDocumentation(-1)[0]
  79. print(desc)
  80. print(
  81. " %s, lcid=%s, major=%s, minor=%s"
  82. % (tlbSpec.clsid, tlbSpec.lcid, tlbSpec.major, tlbSpec.minor)
  83. )
  84. print(" >>> # Use these commands in Python code to auto generate .py support")
  85. print(" >>> from win32com.client import gencache")
  86. print(
  87. " >>> gencache.EnsureModule('%s', %s, %s, %s)"
  88. % (tlbSpec.clsid, tlbSpec.lcid, tlbSpec.major, tlbSpec.minor)
  89. )
  90. class SimpleProgress(genpy.GeneratorProgress):
  91. """A simple progress class prints its output to stderr"""
  92. def __init__(self, verboseLevel):
  93. self.verboseLevel = verboseLevel
  94. def Close(self):
  95. pass
  96. def Finished(self):
  97. if self.verboseLevel > 1:
  98. sys.stderr.write("Generation complete..\n")
  99. def SetDescription(self, desc, maxticks=None):
  100. if self.verboseLevel:
  101. sys.stderr.write(desc + "\n")
  102. def Tick(self, desc=None):
  103. pass
  104. def VerboseProgress(self, desc, verboseLevel=2):
  105. if self.verboseLevel >= verboseLevel:
  106. sys.stderr.write(desc + "\n")
  107. def LogBeginGenerate(self, filename):
  108. self.VerboseProgress("Generating to %s" % filename, 1)
  109. def LogWarning(self, desc):
  110. self.VerboseProgress("WARNING: " + desc, 1)
  111. class GUIProgress(SimpleProgress):
  112. def __init__(self, verboseLevel):
  113. # Import some modules we need to we can trap failure now.
  114. import pywin # nopycln: import
  115. import win32ui
  116. SimpleProgress.__init__(self, verboseLevel)
  117. self.dialog = None
  118. def Close(self):
  119. if self.dialog is not None:
  120. self.dialog.Close()
  121. self.dialog = None
  122. def Starting(self, tlb_desc):
  123. SimpleProgress.Starting(self, tlb_desc)
  124. if self.dialog is None:
  125. from pywin.dialogs import status
  126. self.dialog = status.ThreadedStatusProgressDialog(tlb_desc)
  127. else:
  128. self.dialog.SetTitle(tlb_desc)
  129. def SetDescription(self, desc, maxticks=None):
  130. self.dialog.SetText(desc)
  131. if maxticks:
  132. self.dialog.SetMaxTicks(maxticks)
  133. def Tick(self, desc=None):
  134. self.dialog.Tick()
  135. if desc is not None:
  136. self.dialog.SetText(desc)
  137. def GetTypeLibsForSpec(arg):
  138. """Given an argument on the command line (either a file name, library
  139. description, or ProgID of an object) return a list of actual typelibs
  140. to use."""
  141. typelibs = []
  142. try:
  143. try:
  144. tlb = pythoncom.LoadTypeLib(arg)
  145. spec = selecttlb.TypelibSpec(None, 0, 0, 0)
  146. spec.FromTypelib(tlb, arg)
  147. typelibs.append((tlb, spec))
  148. except pythoncom.com_error:
  149. # See if it is a description
  150. tlbs = selecttlb.FindTlbsWithDescription(arg)
  151. if len(tlbs) == 0:
  152. # Maybe it is the name of a COM object?
  153. try:
  154. ob = Dispatch(arg)
  155. # and if so, it must support typelib info
  156. tlb, index = ob._oleobj_.GetTypeInfo().GetContainingTypeLib()
  157. spec = selecttlb.TypelibSpec(None, 0, 0, 0)
  158. spec.FromTypelib(tlb)
  159. tlbs.append(spec)
  160. except pythoncom.com_error:
  161. pass
  162. if len(tlbs) == 0:
  163. print("Could not locate a type library matching '%s'" % (arg))
  164. for spec in tlbs:
  165. # Version numbers not always reliable if enumerated from registry.
  166. # (as some libs use hex, other's dont. Both examples from MS, of course.)
  167. if spec.dll is None:
  168. tlb = pythoncom.LoadRegTypeLib(
  169. spec.clsid, spec.major, spec.minor, spec.lcid
  170. )
  171. else:
  172. tlb = pythoncom.LoadTypeLib(spec.dll)
  173. # We have a typelib, but it may not be exactly what we specified
  174. # (due to automatic version matching of COM). So we query what we really have!
  175. attr = tlb.GetLibAttr()
  176. spec.major = attr[3]
  177. spec.minor = attr[4]
  178. spec.lcid = attr[1]
  179. typelibs.append((tlb, spec))
  180. return typelibs
  181. except pythoncom.com_error:
  182. t, v, tb = sys.exc_info()
  183. sys.stderr.write("Unable to load type library from '%s' - %s\n" % (arg, v))
  184. tb = None # Storing tb in a local is a cycle!
  185. sys.exit(1)
  186. def GenerateFromTypeLibSpec(
  187. typelibInfo,
  188. file=None,
  189. verboseLevel=None,
  190. progressInstance=None,
  191. bUnicodeToString=None,
  192. bForDemand=bForDemandDefault,
  193. bBuildHidden=1,
  194. ):
  195. assert bUnicodeToString is None, "this is deprecated and will go away"
  196. if verboseLevel is None:
  197. verboseLevel = 0 # By default, we use no gui and no verbose level!
  198. if bForDemand and file is not None:
  199. raise RuntimeError(
  200. "You can only perform a demand-build when the output goes to the gen_py directory"
  201. )
  202. if isinstance(typelibInfo, tuple):
  203. # Tuple
  204. typelibCLSID, lcid, major, minor = typelibInfo
  205. tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
  206. spec = selecttlb.TypelibSpec(typelibCLSID, lcid, major, minor)
  207. spec.FromTypelib(tlb, str(typelibCLSID))
  208. typelibs = [(tlb, spec)]
  209. elif isinstance(typelibInfo, selecttlb.TypelibSpec):
  210. if typelibInfo.dll is None:
  211. # Version numbers not always reliable if enumerated from registry.
  212. tlb = pythoncom.LoadRegTypeLib(
  213. typelibInfo.clsid,
  214. typelibInfo.major,
  215. typelibInfo.minor,
  216. typelibInfo.lcid,
  217. )
  218. else:
  219. tlb = pythoncom.LoadTypeLib(typelibInfo.dll)
  220. typelibs = [(tlb, typelibInfo)]
  221. elif hasattr(typelibInfo, "GetLibAttr"):
  222. # A real typelib object!
  223. # Could also use isinstance(typelibInfo, PyITypeLib) instead, but PyITypeLib is not directly exposed by pythoncom.
  224. # pythoncom.TypeIIDs[pythoncom.IID_ITypeLib] seems to work
  225. tla = typelibInfo.GetLibAttr()
  226. guid = tla[0]
  227. lcid = tla[1]
  228. major = tla[3]
  229. minor = tla[4]
  230. spec = selecttlb.TypelibSpec(guid, lcid, major, minor)
  231. typelibs = [(typelibInfo, spec)]
  232. else:
  233. typelibs = GetTypeLibsForSpec(typelibInfo)
  234. if progressInstance is None:
  235. progressInstance = SimpleProgress(verboseLevel)
  236. progress = progressInstance
  237. bToGenDir = file is None
  238. for typelib, info in typelibs:
  239. gen = genpy.Generator(typelib, info.dll, progress, bBuildHidden=bBuildHidden)
  240. if file is None:
  241. this_name = gencache.GetGeneratedFileName(
  242. info.clsid, info.lcid, info.major, info.minor
  243. )
  244. full_name = os.path.join(gencache.GetGeneratePath(), this_name)
  245. if bForDemand:
  246. try:
  247. os.unlink(full_name + ".py")
  248. except os.error:
  249. pass
  250. try:
  251. os.unlink(full_name + ".pyc")
  252. except os.error:
  253. pass
  254. try:
  255. os.unlink(full_name + ".pyo")
  256. except os.error:
  257. pass
  258. if not os.path.isdir(full_name):
  259. os.mkdir(full_name)
  260. outputName = os.path.join(full_name, "__init__.py")
  261. else:
  262. outputName = full_name + ".py"
  263. fileUse = gen.open_writer(outputName)
  264. progress.LogBeginGenerate(outputName)
  265. else:
  266. fileUse = file
  267. worked = False
  268. try:
  269. gen.generate(fileUse, bForDemand)
  270. worked = True
  271. finally:
  272. if file is None:
  273. gen.finish_writer(outputName, fileUse, worked)
  274. importlib.invalidate_caches()
  275. if bToGenDir:
  276. progress.SetDescription("Importing module")
  277. gencache.AddModuleToCache(info.clsid, info.lcid, info.major, info.minor)
  278. progress.Close()
  279. def GenerateChildFromTypeLibSpec(
  280. child, typelibInfo, verboseLevel=None, progressInstance=None, bUnicodeToString=None
  281. ):
  282. assert bUnicodeToString is None, "this is deprecated and will go away"
  283. if verboseLevel is None:
  284. verboseLevel = (
  285. 0 # By default, we use no gui, and no verbose level for the children.
  286. )
  287. if type(typelibInfo) == type(()):
  288. typelibCLSID, lcid, major, minor = typelibInfo
  289. tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
  290. else:
  291. tlb = typelibInfo
  292. tla = typelibInfo.GetLibAttr()
  293. typelibCLSID = tla[0]
  294. lcid = tla[1]
  295. major = tla[3]
  296. minor = tla[4]
  297. spec = selecttlb.TypelibSpec(typelibCLSID, lcid, major, minor)
  298. spec.FromTypelib(tlb, str(typelibCLSID))
  299. typelibs = [(tlb, spec)]
  300. if progressInstance is None:
  301. progressInstance = SimpleProgress(verboseLevel)
  302. progress = progressInstance
  303. for typelib, info in typelibs:
  304. dir_name = gencache.GetGeneratedFileName(
  305. info.clsid, info.lcid, info.major, info.minor
  306. )
  307. dir_path_name = os.path.join(gencache.GetGeneratePath(), dir_name)
  308. progress.LogBeginGenerate(dir_path_name)
  309. gen = genpy.Generator(typelib, info.dll, progress)
  310. gen.generate_child(child, dir_path_name)
  311. progress.SetDescription("Importing module")
  312. importlib.invalidate_caches()
  313. __import__("win32com.gen_py." + dir_name + "." + child)
  314. progress.Close()
  315. def main():
  316. import getopt
  317. hiddenSpec = 1
  318. outputName = None
  319. verboseLevel = 1
  320. doit = 1
  321. bForDemand = bForDemandDefault
  322. try:
  323. opts, args = getopt.getopt(sys.argv[1:], "vo:huiqd")
  324. for o, v in opts:
  325. if o == "-h":
  326. hiddenSpec = 0
  327. elif o == "-o":
  328. outputName = v
  329. elif o == "-v":
  330. verboseLevel = verboseLevel + 1
  331. elif o == "-q":
  332. verboseLevel = verboseLevel - 1
  333. elif o == "-i":
  334. if len(args) == 0:
  335. ShowInfo(None)
  336. else:
  337. for arg in args:
  338. ShowInfo(arg)
  339. doit = 0
  340. elif o == "-d":
  341. bForDemand = not bForDemand
  342. except (getopt.error, error) as msg:
  343. sys.stderr.write(str(msg) + "\n")
  344. usage()
  345. if bForDemand and outputName is not None:
  346. sys.stderr.write("Can not use -d and -o together\n")
  347. usage()
  348. if not doit:
  349. return 0
  350. if len(args) == 0:
  351. rc = selecttlb.SelectTlb()
  352. if rc is None:
  353. sys.exit(1)
  354. args = [rc]
  355. if outputName is not None:
  356. path = os.path.dirname(outputName)
  357. if path != "" and not os.path.exists(path):
  358. os.makedirs(path)
  359. if sys.version_info > (3, 0):
  360. f = open(outputName, "wt", encoding="mbcs")
  361. else:
  362. import codecs # not available in py3k.
  363. f = codecs.open(outputName, "w", "mbcs")
  364. else:
  365. f = None
  366. for arg in args:
  367. GenerateFromTypeLibSpec(
  368. arg,
  369. f,
  370. verboseLevel=verboseLevel,
  371. bForDemand=bForDemand,
  372. bBuildHidden=hiddenSpec,
  373. )
  374. if f:
  375. f.close()
  376. if __name__ == "__main__":
  377. rc = main()
  378. if rc:
  379. sys.exit(rc)
  380. sys.exit(0)