PageRenderTime 72ms CodeModel.GetById 21ms RepoModel.GetById 6ms app.codeStats 0ms

/doc/makeMetaFromSource.py

http://r-orange.googlecode.com/
Python | 451 lines | 444 code | 3 blank | 4 comment | 1 complexity | 7fb626ec93a55cac0860fda952844243 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, GPL-2.0, LGPL-2.1
  1. """
  2. Make meta files from source files.
  3. """
  4. import re, os
  5. import xml.dom.minidom
  6. from docutils.core import publish_string, publish_parts
  7. doc = None
  8. document = None
  9. overrides = {}#{'stylesheet_path': ','.join([os.path.join(os.path.split(os.path.split(root)[0])[0], 'doc', 'userHelpStyles', 'userHelpCSS.css')])}
  10. header = """
  11. """
  12. sidebar = """
  13. <a href="http://www.red-r.org"><img src="../../../../canvas/icons/CanvasIcon.png" alt="Red-R.org"/></a></br>
  14. <a href="http://www.red-r.org/Documentation">Red-R Documentation</a></br>
  15. <a href="../../../index.html">Packages</a></br>
  16. <a href="../index.html">Parent Package</a>
  17. """
  18. getDirective = re.compile(r'.. (?P<directive>.+?)::', re.DOTALL)
  19. getKeyValue = re.compile(r'\s:(?P<key>.+?):\s`(?P<value>.+?)`', re.DOTALL)
  20. getSignalType = re.compile(r'self\.(?P<type>.+?)s.add', re.DOTALL)
  21. getQuote = re.compile(r"""(_\()?['\"](?P<quote>.+?)['"]""", re.DOTALL)
  22. getGUIClass = re.compile(r'redRGUI\.(?P<class>[a-zA-Z\.]+)', re.DOTALL)
  23. getLabel = re.compile(r'label\s*=\s*(?P<label>.+?)[,\)]', re.DOTALL)
  24. getWidgetXML = re.compile(r'(<widgetXML>.*</widgetXML>)', re.DOTALL)
  25. getBlock = re.compile(r'""".*?"""[\s\n]*?.*?(?=""")|(.*?\n\s*\n)', re.DOTALL)
  26. getSignalClass = re.compile(r'signals\.(?P<signalClass>.+?)[,\]\)]')
  27. getRawRST = re.compile(r'\s::rawrst::(?P<rawrst>.+?)(?=""")', re.DOTALL)
  28. getRLibraryCall = re.compile(r'self\.require_librarys\((?P<libs>.+?)\)', re.DOTALL)
  29. getHelpDoc = re.compile(r'.. helpdoc::(?P<helpdoc>.*?)"""', re.DOTALL)
  30. getName = re.compile(r'<name>\s*(?P<name>.+?)\s*</name>', re.DOTALL)
  31. getAuthor = re.compile(r'<author>(?P<authorBlock>.*?)</author>', re.DOTALL)
  32. getAuthorName = re.compile(r'<name>(?P<authorname>.*?)</name>', re.DOTALL)
  33. getAuthorContact = re.compile(r'<contact>(?P<authorcontact>.*?)</contact>', re.DOTALL)
  34. getSignalXML = re.compile(r'<signalXML>.*?</signalXML>', re.DOTALL)
  35. getSignalParent = re.compile(r'.. signalClass:: *(?P<signalClass>.+?)[ "]')
  36. getSignalConvertTo = re.compile(r'.. convertTo:: *`(?P<convertTo>.+?)`')
  37. getSignalConvertFrom = re.compile(r'.. convertFrom:: *`(?P<convertFrom>.+?)`')
  38. getQTClass = re.compile(r'class .+?\((?P<parent>Q[A-Xa-z]+?)[, \)]')
  39. def _getXMLDirective(string):
  40. """Returns an rst directive or None in the form \.\.\ (?P<directive>.*?)::"""
  41. match = re.search(getDirective, string)
  42. if not match: return None
  43. else: return match.group('directive')
  44. def _getKeyValuePairs(string):
  45. d = {}
  46. for m in re.finditer(getKeyValue, string):
  47. d[m.group('key')] = m.group('value')
  48. return d
  49. def _getRvariableNames(string):
  50. """Matches the names of R variables in the setRvariableNames declaration. Returns a a list of names"""
  51. match = re.search(r'self\.setRvariableNames\(\[.+?\)\]', string)
  52. if not match: return None
  53. names = []
  54. for m in re.finditer(getQuote, match.group()):
  55. names.append(m.group('quote'))
  56. return names
  57. def _getRRSignals(string):
  58. """returns a dict with values: type, name, signals, description, """
  59. d = {}
  60. ## get the type
  61. type = re.search(getSignalType, string)
  62. if type:
  63. d['type'] = type.group('type')
  64. d.update(_getKeyValuePairs(string))
  65. if not 'name' in d:
  66. nameGroup = re.search(r'put\(.*?,\s*(?P<nameGroup>.+?),', string)
  67. if nameGroup:
  68. nameString = re.search(getQuote, nameGroup.group('nameGroup'))
  69. if nameString:
  70. d['name'] = nameString.group('quote')
  71. if not 'name' in d: d['name'] = ''
  72. if not 'signals' in d: d['signals'] = [m.group('signalClass') for m in re.finditer(getSignalClass, string)]
  73. if not 'description' in d: d['description'] = ''
  74. rawRST = re.search(getRawRST, string) # return any raw rst string
  75. if rawRST: d['rst'] = rawRST.group('rawrst')
  76. else: d['rst'] = ''
  77. return d
  78. def _getRRGUISettings(string):
  79. """Parses an rrgui setting and returns a tuple of class, label or None"""
  80. d = {}
  81. d.update(_getKeyValuePairs(string))
  82. if not 'class' in d:
  83. guiClass = re.search(getGUIClass, string)
  84. if guiClass: d.update(guiClass.groupdict())
  85. if not 'class' in d: d['class'] = ''
  86. if not 'label' in d:
  87. guiLabel = re.search(getLabel, string)
  88. if guiLabel:
  89. label = re.search(getQuote, guiLabel.group('label'))
  90. if label: d['label'] = label.group('quote')
  91. if not 'label' in d: d['label'] = ''
  92. if not 'description' in d: d['description'] = 'No description entered for this GUI parameter'
  93. rawRST = re.search(getRawRST, string) # return any raw rst string
  94. if rawRST: d['rst'] = rawRST.group('rawrst')
  95. else: d['rst'] = ''
  96. return d
  97. def getXMLText(nodelist):
  98. """A function to handle retrieval of xml text.
  99. .. note::
  100. This is such a ubiquitous function that we may consider making our own xml handler with this built in.
  101. """
  102. rc = ''
  103. for node in nodelist:
  104. if node.nodeType == node.TEXT_NODE:
  105. rc = rc + node.data
  106. rc = unicode(rc).strip()
  107. return rc
  108. def _parsefile(myFile):
  109. d = {'widgetXML':'', 'signals':[], 'rrgui':[], 'rrvarnammes':[], 'rpackages':[], 'helpdoc':[], 'name':'', 'author':[]}
  110. # parse the widgetXML and handle that.
  111. widgetXML = re.search(getWidgetXML, myFile)
  112. if not widgetXML: raise Exception('Widget does not have a widgetXML section')
  113. d['widgetXML'] = widgetXML.group(1)
  114. # print d['widgetXML']
  115. widgetMetaXML = xml.dom.minidom.parseString(d['widgetXML'])
  116. d['name'] = getXMLText(widgetMetaXML.getElementsByTagName('name')[0].childNodes)
  117. d['icon'] = getXMLText(widgetMetaXML.getElementsByTagName('icon')[0].childNodes)
  118. d['summary'] = getXMLText(widgetMetaXML.getElementsByTagName('summary')[0].childNodes)
  119. d['tags'] = [] ## will be a list of tuples in the form (<tag>, <priority>); ('Data Input', 4)
  120. for tag in widgetMetaXML.getElementsByTagName('tags')[0].childNodes:
  121. if getXMLText(tag.childNodes) != '':
  122. if tag.hasAttribute('priority'):
  123. d['tags'].append((getXMLText(tag.childNodes), int(tag.getAttribute('priority'))))
  124. else:
  125. d['tags'].append((getXMLText(tag.childNodes), 0))
  126. d['author'] = [] ## will be a list of tuples in the form (<tag>, <priority>); ('Data Input', 4)
  127. for author in widgetMetaXML.getElementsByTagName('author'):
  128. #print author
  129. #print getXMLText(author.getElementsByTagName('name')[0].childNodes)
  130. d['author'].append((getXMLText(author.getElementsByTagName('authorname')[0].childNodes),
  131. getXMLText(author.getElementsByTagName('authorcontact')[0].childNodes)))
  132. # get any loaded R libraries, wrapped in a try because some widgets might not load R libraries
  133. try:
  134. for m in re.finditer(getRLibraryCall, myFile):
  135. for q in re.finditer(getQuote, m.group()):
  136. d['rpackages'].append(q.group('quote'))
  137. except:pass
  138. # get the help documentation, wrapped in a try because some widget might not have any help documentation
  139. try:
  140. for m in re.finditer(getHelpDoc, myFile):
  141. d['helpdoc'].append(m.group('helpdoc'))
  142. except: pass
  143. for m in re.finditer(getBlock, myFile.replace('\r', '')):
  144. """ m is a block of code, that is a docstring followed by one block of code. The block must start with tripple quotes."""
  145. if not re.search(re.compile(r'\s*"""', re.DOTALL), m.group().split('\n')[0]): continue # """The docstring must be at the beginning of the block"""
  146. string = m.group()
  147. """if the string contains a directive we should find out what the directive is and then how to handle it."""
  148. directive = _getXMLDirective(string)
  149. if directive == None: continue
  150. if directive in ['rrsignals', 'rrgui']: # it's one of ours!!
  151. """if there are other options in the docstring then they belong to this directive, we try to get them"""
  152. if directive == 'rrsignals':
  153. d['signals'].append(_getRRSignals(string))
  154. elif directive == 'rrgui':
  155. d['rrgui'].append(_getRRGUISettings(string))
  156. # print d
  157. # asdfafd
  158. return d
  159. def parseWidgetFile(filename, outputXML, userHelp,devHelp):
  160. """Reads a file and parses out the relevant widget xml settings, writes to the file output an xml document representing the parsed data. Prints success message on success."""
  161. global doc
  162. fileStrings = []
  163. with open(filename, 'r') as f:
  164. myFile = f.read()
  165. moduleName = os.path.basename(filename).split('.')[0]
  166. packgeName = os.path.split(os.path.split(os.path.split(filename)[0])[0])[1]
  167. """Pass the list of strings to the parser to extract the relevant structure"""
  168. d = _parsefile(myFile)
  169. with open(outputXML, 'w') as f:
  170. f.write(makeXML(d))
  171. with open(userHelp, 'w') as f:
  172. helprst = makeHelp(d)
  173. f.write(helprst)
  174. with open(devHelp, 'w') as f:
  175. output = """%(WIDGETNAME)s
  176. =================================
  177. .. automodule:: libraries.%(PACKAGENAME)s.widgets.%(WIDGETNAME)s
  178. :members:
  179. :undoc-members:
  180. :show-inheritance:
  181. """ % {'WIDGETNAME':moduleName, 'PACKAGENAME':packgeName}
  182. f.write(output)
  183. print 'Success for %s' % filename
  184. return d['name']
  185. def makeXML(d):
  186. """Makes an xml file as plain text"""
  187. """make the header"""
  188. s = """<?xml version="1.0" encoding="ISO-8859-1"?>
  189. <?xml-stylesheet type="text/xsl" href="../../help.xsl"?>
  190. <documentation>"""
  191. s += d['widgetXML']
  192. """put in the signal classes"""
  193. s += '\n<signals>\n'
  194. for rs in d['signals']:
  195. s += '<%(type)s>\n' % rs
  196. s += '\t<name>%(name)s</name>\n' % rs
  197. s += '\t<description>\n%(description)s\n\t</description>\n' % rs
  198. s += '\t<signalClass>%s</signalClass>\n' % ','.join(rs['signals'])
  199. s += '</%(type)s>\n' % rs
  200. s += '</signals>\n'
  201. s += '</documentation>'
  202. return s
  203. def makeHelp(d):
  204. """Makes a help document from the source as a .rst document"""
  205. s = ''
  206. s += '%s\n%s\n\n' % (d['name'], ')'*len(d['name']))
  207. s += 'Authors\n((((((((((((\n\n'
  208. for n, c in d['author']:
  209. s += '%s, %s\n\n' % (n, c)
  210. s += '\nDocumentation\n((((((((((((((((((\n\n'
  211. if len(d['helpdoc']) == 0:
  212. s += 'No help documentation entered for this widget'
  213. else:
  214. s += '\n'.join(d['helpdoc'])
  215. s += '\n\n'
  216. s += 'Interface\n((((((((((((\n\n'
  217. for gui in d['rrgui']:
  218. s += '%s\n%s\n\n' % (gui['label'], '}'*len(gui['label']))
  219. s += '**Description**\n\n'
  220. s += '%s\n\n' % gui['description']
  221. s += '%s\n\n' % gui['rst']
  222. s += 'Class: `%s`_\n\n' % gui['class']
  223. s += 'Signals\n((((((((((((((\n\n'
  224. for sig in d['signals']:
  225. s += '%s\n%s\n\n' % (sig['name'], '}'*len(sig['name']))
  226. s += 'Classes:'
  227. for ss in sig['signals']:
  228. s += '`%s`_ ' % ss
  229. s+= '\n\n'
  230. s += '**Description**\n\n'
  231. s += '%s\n\n' % sig['description']
  232. s += '%s\n\n' % sig['rst']
  233. s += 'R Packages\n((((((((((((((\n\n'
  234. s += ','.join(d['rpackages'])
  235. s += '\n\n'
  236. for gui in d['rrgui']:
  237. if gui['class'] =='':
  238. s += '.. _%s: #\n\n' % (gui['class'])
  239. else:
  240. (package,guiClass) = gui['class'].split('.')
  241. s += '.. _%s: ../../../../../libraries/%s/help/userDoc/qtWidgets/%s.html\n\n' % (gui['class'],package,guiClass)
  242. for sig in d['signals']:
  243. # print sig, len(sig['signals'])
  244. if len(sig['signals']) ==0:
  245. s += '.. _%s: #\n\n' % (sig['signals'])
  246. else:
  247. for ss in sig['signals']:
  248. (package,sigClass) = ss.split('.')
  249. s += '.. _%s: ../../../../libraries/%s/help/userDoc/signalClasses/%s.html\n\n' % (ss, package,sigClass)
  250. return s
  251. def parseSignalFile(filename, userHelp,devHelp, outputXML):
  252. with open(filename, 'r') as f:
  253. myFile = f.read()
  254. moduleName = os.path.basename(filename).split('.')[0]
  255. packgeName = os.path.split(os.path.split(os.path.split(filename)[0])[0])[1]
  256. d = {'helpdoc':[], 'signalClass':[], 'name':os.path.split(filename)[1], 'convertFrom':[], 'convertTo':[]}
  257. try:
  258. for m in re.finditer(getHelpDoc, myFile):
  259. d['helpdoc'].append(m.group('helpdoc'))
  260. except: pass
  261. try:
  262. for m in re.finditer(getSignalParent, myFile):
  263. d['signalClass'].append(m.group('signalClass').strip())
  264. except: pass
  265. try:
  266. d['convertTo'] = [c.strip() for c in re.search(getSignalConvertTo, myFile).group('convertTo').split(',')]
  267. except: pass
  268. try:
  269. d['convertFrom'] = [c.strip() for c in re.search(getSignalConvertFrom, myFile).group('convertFrom').split(',')]
  270. except: pass
  271. with open(userHelp, 'w') as f:
  272. f.write(makeSignalHelp(d))
  273. with open(devHelp, 'w') as f:
  274. output = """%(WIDGETNAME)s
  275. =================================
  276. .. automodule:: libraries.%(PACKAGENAME)s.signalClasses.%(WIDGETNAME)s
  277. :members:
  278. :undoc-members:
  279. :show-inheritance:
  280. """ % {'WIDGETNAME':moduleName, 'PACKAGENAME':packgeName}
  281. f.write(output)
  282. with open(outputXML, 'w') as f:
  283. f.write(makeSignalXML(d))
  284. print 'Success for %s' % filename
  285. return d['name']
  286. def parseQTWidgetFile(filename, userHelp,devHelp, outputXML):
  287. with open(filename, 'r') as f:
  288. myFile = f.read()
  289. moduleName = os.path.basename(filename).split('.')[0]
  290. packgeName = os.path.split(os.path.split(os.path.split(filename)[0])[0])[1]
  291. d = {'helpdoc':[], 'name':os.path.split(filename)[1], 'parent':''}
  292. try:
  293. for m in re.finditer(getHelpDoc, myFile):
  294. d['helpdoc'].append(m.group('helpdoc'))
  295. except: pass
  296. try:
  297. d['parent'] = re.search(getQTClass, myFile).group('parent')
  298. except: pass
  299. with open(userHelp, 'w') as f:
  300. helprst = makeQTHelp(d)
  301. f.write(helprst)
  302. with open(outputXML, 'w') as f:
  303. f.write(makeQTXML(d))
  304. with open(devHelp, 'w') as f:
  305. output = """%(WIDGETNAME)s
  306. =================================
  307. .. automodule:: libraries.%(PACKAGENAME)s.qtWidgets.%(WIDGETNAME)s
  308. :members:
  309. :undoc-members:
  310. :show-inheritance:
  311. """ % {'WIDGETNAME':moduleName, 'PACKAGENAME':packgeName}
  312. f.write(output)
  313. return d['name']
  314. def makeQTHelp(d):
  315. """Takes a dict of helpdoc, name, and parent and makes an rst file for documentation."""
  316. s = header
  317. s += '%s\n%s\n\n' % (d['name'], ')'*len(d['name']))
  318. s += '.. contents::\n\n'
  319. if d['parent'] == '':
  320. s += 'Non standard parent as parent class.\n\n'
  321. else:
  322. s += 'Inherits from `%s`_.\n\n' % d['parent']
  323. s += '.. _`%s`: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/%s.html\n\n' % (d['parent'], d['parent'].lower())
  324. s += '\nDocumentation\n((((((((((((((((((\n\n'
  325. if len(d['helpdoc']) == 0:
  326. s += 'No help documentation entered for this onject'
  327. else:
  328. s += '\n'.join(d['helpdoc'])
  329. s += '\n\n'
  330. return s
  331. def makeQTXML(d):
  332. """Makes an xml file as plain text"""
  333. """make the header"""
  334. s = """<?xml version="1.0" encoding="ISO-8859-1"?>
  335. <?xml-stylesheet type="text/xsl" href="../../help.xsl"?>
  336. <documentation>"""
  337. s += '</documentation>'
  338. return s
  339. def makeSignalHelp(d):
  340. """Makes a signal help file using a dict containing two lists, helpdoc and signalClass"""
  341. s = header
  342. s += '%s\n%s\n\n' % (d['name'], ')'*len(d['name']))
  343. s += '.. contents::\n\n'
  344. s += 'Dependent signals\n(((((((((((((((((((((((\n\n'
  345. for sig in d['signalClass']:
  346. s += '%s\n%s\n\n' % (sig+'_', '}'*(len(sig)+5))
  347. s += '\n\nConvert To\n{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{\n\n'
  348. for sig in d['convertTo']:
  349. s += '%s\n' % (sig+'_')
  350. s += '\n\nConvert From\n{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{\n\n'
  351. for sig in d['convertFrom']:
  352. s += '%s\n' % (sig+'_')
  353. s += '\nDocumentation\n((((((((((((((((((\n\n'
  354. if len(d['helpdoc']) == 0:
  355. s += 'No help documentation entered for this signal class'
  356. else:
  357. s += '\n'.join(d['helpdoc'])
  358. s += '\n\n'
  359. for sig in d['signalClass']:
  360. s += '.. _%s: ../../../../%s/help/userDoc/signalClasses/%s.html\n\n' % (sig, sig.split(':')[0], sig.split(':')[1])
  361. for sig in d['convertTo']:
  362. s += '.. _%s: ../../../../%s/help/userDoc/signalClasses/%s.html\n\n' % (sig, sig.split(':')[0], sig.split(':')[1])
  363. for sig in d['convertFrom']:
  364. s += '.. _%s: ../../../../%s/help/userDoc/signalClasses/%s.html\n\n' % (sig, sig.split(':')[0], sig.split(':')[1])
  365. return s
  366. def makeSignalXML(d):
  367. """Makes an xml file as plain text"""
  368. """make the header"""
  369. s = """<?xml version="1.0" encoding="ISO-8859-1"?>
  370. <?xml-stylesheet type="text/xsl" href="../../help.xsl"?>
  371. <documentation>"""
  372. """put in the signal classes"""
  373. s += '<signals>'
  374. for rs in d['signalClass']:
  375. s += '<signal>%s</signal>\n' % rs
  376. s += '</signals>\n'
  377. s += '<convertTo>%s</convertTo>\n' % ','.join(d['convertTo'])
  378. s += '<convertFrom>%s</convertFrom>\n' % ','.join(d['convertFrom'])
  379. s += '</documentation>'
  380. return s
  381. # def test(path):
  382. # parseFile(path, 'outputXML.xml', 'outputTest.txt', 'outputDevel.txt')
  383. # import sys
  384. # test(sys.argv[1])