build_modules_xml.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. """
  2. @package tools.build_modules_xml
  3. @brief Builds XML metadata of GRASS modules. Runs only during compilation.
  4. (C) 2013 by the GRASS Development Team
  5. This program is free software under the GNU General Public License
  6. (>=v2). Read the file COPYING that comes with GRASS for details.
  7. @author Vaclav Petras <wenzeslaus gmail.com>
  8. @author Anna Petrasova <kratochanna gmail.com>
  9. """
  10. from __future__ import print_function
  11. import sys
  12. from datetime import datetime
  13. import grass.script.core as gcore
  14. import grass.script.task as gtask
  15. def escapeXML(text):
  16. """This is a duplicate of function in core/toolboxes.
  17. >>> escapeXML('<>&')
  18. '&amp;lt;&gt;&amp;'
  19. """
  20. return text.replace('<', '&lt;').replace("&", '&amp;').replace(">", '&gt;')
  21. def do_doctest_gettext_workaround():
  22. """This is a duplicate of function in core/toolboxes."""
  23. def new_displayhook(string):
  24. """A replacement for default `sys.displayhook`"""
  25. if string is not None:
  26. sys.stdout.write("%r\n" % (string,))
  27. def new_translator(string):
  28. """A fake gettext underscore function."""
  29. return string
  30. sys.displayhook = new_displayhook
  31. sys.__displayhook__ = new_displayhook
  32. import __builtin__
  33. __builtin__._ = new_translator
  34. def parse_modules(fd):
  35. """Writes metadata to xml file."""
  36. # TODO: what about ms windows? does gtask handle this?
  37. mlist = list(gcore.get_commands()[0])
  38. indent = 4
  39. for m in sorted(mlist):
  40. # TODO: get rid of g.mapsets_picker.py
  41. if m == 'g.mapsets_picker.py' or m == 'g.parser':
  42. continue
  43. desc, keyw = get_module_metadata(m)
  44. fd.write('%s<module-item name="%s">\n' % (' ' * indent, m))
  45. indent += 4
  46. fd.write('%s<module>%s</module>\n' % (' ' * indent, m))
  47. fd.write(
  48. '%s<description>%s</description>\n' %
  49. (' ' * indent, escapeXML(desc)))
  50. fd.write('%s<keywords>%s</keywords>\n' %
  51. (' ' * indent, escapeXML(','.join(keyw))))
  52. indent -= 4
  53. fd.write('%s</module-item>\n' % (' ' * indent))
  54. def get_module_metadata(name):
  55. """
  56. >>> get_module_metadata('g.region')
  57. ('Manages the boundary definitions for the geographic region.', ['general', 'settings'])
  58. >>> get_module_metadata('m.proj')
  59. ('Converts coordinates from one projection to another (cs2cs frontend).', ['miscellaneous', 'projection'])
  60. """
  61. try:
  62. task = gtask.parse_interface(name)
  63. except:
  64. sys.stderr.write("Cannot parse interface for module %s. Empty strings"
  65. " will be placed instead of description and keywords."
  66. "\n" % name)
  67. return '', ''
  68. return task.get_description(full=True), \
  69. task.get_keywords()
  70. def header(fd):
  71. fd.write('<?xml version="1.0" encoding="UTF-8"?>\n')
  72. fd.write('<!DOCTYPE module-items SYSTEM "module_items.dtd">\n')
  73. fd.write(
  74. '<!--This file is automatically generated using %s-->\n' %
  75. sys.argv[0])
  76. # g.version -r is crashing, commenting this block for now
  77. # vInfo = gcore.version()
  78. # fd.write('<!--version="%s" revision="%s" date="%s"-->\n' % \
  79. # (vInfo['version'].split('.')[0],
  80. # vInfo['revision'],
  81. # datetime.now()))
  82. fd.write('<module-items>\n')
  83. def footer(fd):
  84. fd.write('</module-items>\n')
  85. def doc_test():
  86. """Tests the module using doctest
  87. :return: a number of failed tests
  88. """
  89. import doctest
  90. do_doctest_gettext_workaround()
  91. return doctest.testmod().failed
  92. def module_test():
  93. grass_commands = gcore.get_commands()[0]
  94. if not 'g.region' in grass_commands:
  95. print("No g.region")
  96. return 1
  97. if not 'm.proj' in grass_commands:
  98. print("No m.proj")
  99. return 1
  100. if not 't.rast.univar' in grass_commands:
  101. print("No t.rast.univar")
  102. return 1
  103. print(get_module_metadata('g.region'))
  104. print(get_module_metadata('m.proj'))
  105. print(get_module_metadata('t.rast.univar'))
  106. def main():
  107. fh = sys.stdout
  108. header(fh)
  109. parse_modules(fh)
  110. footer(fh)
  111. return 0
  112. if __name__ == "__main__":
  113. if len(sys.argv) > 1:
  114. if sys.argv[1] == 'doctest':
  115. sys.exit(doc_test())
  116. elif sys.argv[1] == 'test':
  117. sys.exit(module_test())
  118. else:
  119. gcore.fatal('Unrecognized parameter.')
  120. sys.exit(main())