PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/coala_bears_create/coala_bears_create.py

https://gitlab.com/coala/coala-bear-management
Python | 299 lines | 256 code | 15 blank | 28 comment | 7 complexity | be045ece6d7c122807023781316d8bbd MD5 | raw file
  1. import argparse
  2. from functools import lru_cache
  3. import os
  4. import logging
  5. import string
  6. import sys
  7. from coalib.settings.Section import Section
  8. from coalib.misc.DictUtilities import inverse_dicts
  9. from coalib.collecting.Collectors import collect_all_bears_from_sections
  10. from coalib.output.printers.LogPrinter import LogPrinter
  11. from coala_utils.Question import ask_question
  12. from coala_utils.string_processing.StringConverter import StringConverter
  13. from prompt_toolkit import prompt
  14. from prompt_toolkit.contrib.completers import WordCompleter
  15. from prompt_toolkit.validation import ValidationError, Validator
  16. from pygments.style import Style
  17. from pygments.styles.default import DefaultStyle
  18. from pygments.token import Token
  19. from pyprint.NullPrinter import NullPrinter
  20. @lru_cache()
  21. def get_all_bears():
  22. """
  23. Get a dict of bears with the bear class as key.
  24. The dict of bears is only created from the default bear directories
  25. with no additional configurations to coala.
  26. >>> [True for bear in get_all_bears() if 'RuboCopBear' in bear.name]
  27. [True]
  28. :return:
  29. A dict with bear classes as key and the list of sections
  30. as value.
  31. """
  32. log_printer = LogPrinter(NullPrinter())
  33. sections = {'default': Section('default')}
  34. local_bears, global_bears = collect_all_bears_from_sections(
  35. sections, log_printer)
  36. return inverse_dicts(local_bears, global_bears)
  37. def get_languages():
  38. """
  39. Get a list of dict of languages with bear name.
  40. >>> "C++" in get_languages()
  41. True
  42. :return:
  43. A set with all languages present as value.
  44. """
  45. return {language for bear in get_all_bears() for language in bear.LANGUAGES}
  46. def add_status_bar(cli):
  47. """
  48. Add a message to the status bar.
  49. :return:
  50. A status bar of class Token with custom message
  51. """
  52. return [(Token.Toolbar,
  53. "'regex' for parsing messages yielded by executable, 'corrected' "
  54. "for parsing a corrected version of the inputted file")]
  55. def status_bar_languages(cli):
  56. """
  57. Instruction message for languages question.
  58. :return:
  59. A status bar of class Token with custom message
  60. """
  61. return [(Token.Toolbar,
  62. 'Press Escape + Enter to move to next question')]
  63. def get_continuation_tokens(cli, width):
  64. return [(Token, '.' * width)]
  65. class BearTypeValidator(Validator):
  66. def validate(self, document):
  67. """
  68. Display error message if text not valid.
  69. return:
  70. A status bar with validation error message
  71. """
  72. text = document.text.lower()
  73. if text not in ("regex", "corrected"):
  74. raise ValidationError(message='Enter "regex" for regex bears and '
  75. '"corrected" for autocorrect bears.')
  76. class DocumentStyle(Style):
  77. """
  78. Styles for dropdown list and status bar. Styles are populated using a
  79. dictionary with the key being Token subclasses and the value being a list.
  80. """
  81. styles = dict(DefaultStyle.styles)
  82. styles.update({
  83. # hex for background color of highlighted member of dropdown list.
  84. Token.Menu.Completions.Completion.Current: 'bg:#00aaaa #000000',
  85. # hex for background color of dropdown list.
  86. Token.Menu.Completions.Completion: 'bg:#008888 #ffffff',
  87. # hex for background color of bottom status bar.
  88. Token.Toolbar: '#ffffff bg:#333333'
  89. })
  90. def load_template(file):
  91. """
  92. Return lines from file as a string.
  93. :return:
  94. The content of the file as a string.
  95. """
  96. path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
  97. "scaffold-templates", "{}.in".format(file))
  98. with open(path) as temp:
  99. return temp.read()
  100. # To be used when no default value is given
  101. NoDefaultValue = object()
  102. def create_setting(sdesc, stype, sdefault=NoDefaultValue):
  103. """
  104. Create a tuple ``(sdesc, stype)`` for non_optional settings and
  105. ``(sdesc, stype, sdefault)`` for optional settings.
  106. :param sdesc:
  107. Setting description.
  108. :param stype:
  109. Setting type.
  110. :param sdefault:
  111. Setting default value.
  112. :return:
  113. A tuple with the setting information.
  114. """
  115. return ((sdesc, stype) if sdefault is NoDefaultValue
  116. else (sdesc, stype, sdefault))
  117. def main():
  118. # Default values if the user chooses to skip a question.
  119. conf = {
  120. 'name': '',
  121. 'testname': '',
  122. 'description': '',
  123. 'language': (),
  124. 'bear_type': '',
  125. 'executable': '',
  126. 'settings': {},
  127. 'arguments': (),
  128. 'requirements': '',
  129. 'authors': '',
  130. 'authors_emails': '',
  131. 'license': '',
  132. }
  133. parser = argparse.ArgumentParser(
  134. description="Generate a linter bear and test " +
  135. "file using the command line",
  136. formatter_class=argparse.ArgumentDefaultsHelpFormatter)
  137. parser.add_argument('-p', '--path', metavar="path",
  138. help="Path where your files will be generated")
  139. parser.add_argument('-n', '--name', metavar="name",
  140. help='Name of bear')
  141. parser.add_argument('-d', '--desc', metavar="description",
  142. help='Description of bear')
  143. parser.add_argument('-e', '--executable', metavar="",
  144. help='Name of linter executable')
  145. parser.add_argument('-l', '--lang', metavar="language",
  146. help='Language that the bear will be used on')
  147. parser.add_argument('-t', '--type', metavar="type",
  148. help='Type of bear')
  149. parser.add_argument('-ext', '--external', default=False,
  150. action="store_true", help='Creates an external bear ' +
  151. 'instead of a regular one')
  152. args = parser.parse_args()
  153. print('''Welcome to coala-bears-create.
  154. This command line tool will help you configure a coala bear and test file.
  155. Please answer the following questions so this tool can help you automate
  156. generating the files.
  157. ''')
  158. directory = args.path or os.path.abspath(
  159. os.path.expanduser(ask_question(
  160. 'Where do you want to create your new bear? ', default=os.curdir)))
  161. conf['name'] = args.name or ask_question('Name of this bear: ')
  162. conf['description'] = args.desc or (ask_question(
  163. 'Description of the bear: '))
  164. conf['executable'] = args.executable or (
  165. ask_question('Name of the the executable: '))
  166. if not args.external:
  167. conf['bear_type'] = args.type or (ask_question(
  168. 'What is the type of output the external executable ' +
  169. 'produces? (regex/corrected)',
  170. get_bottom_toolbar_tokens=add_status_bar,
  171. style=DocumentStyle,
  172. validator=BearTypeValidator()).lower())
  173. elif ask_question('Do you want to use settings? ', default="No",
  174. typecast=bool):
  175. more_settings = True
  176. setting_strings = ""
  177. while more_settings:
  178. setting_strings += ask_question(
  179. 'Give settings as name|desc|type[|default_value]: ',
  180. multiline=True,
  181. style=DocumentStyle,
  182. get_continuation_tokens=get_continuation_tokens,
  183. get_bottom_toolbar_tokens=status_bar_languages) + "\n"
  184. more_settings = ask_question('Are there more settings?',
  185. default="No", typecast=bool)
  186. setting_strings = setting_strings.split('\n')
  187. for setting_string in (setting_str for setting_str in setting_strings
  188. if len(setting_str) != 0):
  189. setting_string = setting_string.split('|')
  190. sname = setting_string[0]
  191. sdesc = setting_string[1]
  192. stype = setting_string[2]
  193. # check if default value was provided
  194. if len(setting_string) == 4:
  195. sdefault = getattr(__builtins__, stype)(
  196. StringConverter(setting_string[3]))
  197. conf['settings'][sname] = create_setting(sdesc, stype,
  198. sdefault)
  199. else:
  200. conf['settings'][sname] = create_setting(sdesc, stype)
  201. conf['language'] = args.lang or ask_question(
  202. 'Languages that bear will support: ', typecast=set,
  203. completer=WordCompleter(get_languages(), ignore_case=True),
  204. style=DocumentStyle,
  205. multiline=True,
  206. get_continuation_tokens=get_continuation_tokens,
  207. get_bottom_toolbar_tokens=status_bar_languages)
  208. conf['requirements'] = ask_question(
  209. 'Add dependencies used by the executable: ', typecast=set)
  210. conf['authors'] = ask_question(
  211. 'Add Author(s) name(s): ', typecast=set)
  212. conf['authors_emails'] = ask_question(
  213. 'Add Author(s) email(s): ', typecast=set)
  214. conf['license'] = ask_question('Add License name for this code: ')
  215. if not conf['name'].lower().endswith('bear'):
  216. conf['name'] += 'Bear'
  217. conf['testname'] = conf['name'] + 'Test'
  218. bearfilename = conf['name'] + '.py'
  219. testfilename = conf['name'] + 'Test.py'
  220. template_file = 'bearconf' if not args.external else 'externalbearconf.py'
  221. try:
  222. os.makedirs(directory, exist_ok=True)
  223. except OSError as e:
  224. logging.exception('Cannot create directory: {}'.format(e))
  225. return
  226. try:
  227. processed_template = string.Template(load_template(template_file)
  228. ).safe_substitute(conf)
  229. with open(os.path.join(directory, bearfilename), 'w') as template:
  230. template.write(processed_template)
  231. except OSError as e:
  232. logging.exception('Cannot create bear file: {}'.format(e))
  233. return
  234. try:
  235. processed_template = string.Template(load_template('testconf')
  236. ).safe_substitute(conf)
  237. with open(os.path.join(directory, testfilename), 'w') as template:
  238. template.write(processed_template)
  239. except OSError as e:
  240. logging.exception('Cannot create test file: {}'.format(e))
  241. return
  242. print("Successfully completed generating your bear and test file"
  243. " at '{}'. You can now modify the files inside and implement your"
  244. " own bear."
  245. .format(directory))
  246. if __name__ == "__main__":
  247. sys.exit(main())