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.

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # Copyright (C) 2009-2018 the sqlparse authors and contributors
  5. # <see AUTHORS file>
  6. #
  7. # This module is part of python-sqlparse and is released under
  8. # the BSD License: https://opensource.org/licenses/BSD-3-Clause
  9. """Module that contains the command line app.
  10. Why does this file exist, and why not put this in __main__?
  11. You might be tempted to import things from __main__ later, but that will
  12. cause problems: the code will get executed twice:
  13. - When you run `python -m sqlparse` python will execute
  14. ``__main__.py`` as a script. That means there won't be any
  15. ``sqlparse.__main__`` in ``sys.modules``.
  16. - When you import __main__ it will get executed again (as a module) because
  17. there's no ``sqlparse.__main__`` in ``sys.modules``.
  18. Also see (1) from http://click.pocoo.org/5/setuptools/#setuptools-integration
  19. """
  20. import argparse
  21. import sys
  22. from io import TextIOWrapper
  23. from codecs import open, getreader
  24. import sqlparse
  25. from sqlparse.compat import PY2
  26. from sqlparse.exceptions import SQLParseError
  27. # TODO: Add CLI Tests
  28. # TODO: Simplify formatter by using argparse `type` arguments
  29. def create_parser():
  30. _CASE_CHOICES = ['upper', 'lower', 'capitalize']
  31. parser = argparse.ArgumentParser(
  32. prog='sqlformat',
  33. description='Format FILE according to OPTIONS. Use "-" as FILE '
  34. 'to read from stdin.',
  35. usage='%(prog)s [OPTIONS] FILE, ...',
  36. )
  37. parser.add_argument('filename')
  38. parser.add_argument(
  39. '-o', '--outfile',
  40. dest='outfile',
  41. metavar='FILE',
  42. help='write output to FILE (defaults to stdout)')
  43. parser.add_argument(
  44. '--version',
  45. action='version',
  46. version=sqlparse.__version__)
  47. group = parser.add_argument_group('Formatting Options')
  48. group.add_argument(
  49. '-k', '--keywords',
  50. metavar='CHOICE',
  51. dest='keyword_case',
  52. choices=_CASE_CHOICES,
  53. help='change case of keywords, CHOICE is one of {0}'.format(
  54. ', '.join('"{0}"'.format(x) for x in _CASE_CHOICES)))
  55. group.add_argument(
  56. '-i', '--identifiers',
  57. metavar='CHOICE',
  58. dest='identifier_case',
  59. choices=_CASE_CHOICES,
  60. help='change case of identifiers, CHOICE is one of {0}'.format(
  61. ', '.join('"{0}"'.format(x) for x in _CASE_CHOICES)))
  62. group.add_argument(
  63. '-l', '--language',
  64. metavar='LANG',
  65. dest='output_format',
  66. choices=['python', 'php'],
  67. help='output a snippet in programming language LANG, '
  68. 'choices are "python", "php"')
  69. group.add_argument(
  70. '--strip-comments',
  71. dest='strip_comments',
  72. action='store_true',
  73. default=False,
  74. help='remove comments')
  75. group.add_argument(
  76. '-r', '--reindent',
  77. dest='reindent',
  78. action='store_true',
  79. default=False,
  80. help='reindent statements')
  81. group.add_argument(
  82. '--indent_width',
  83. dest='indent_width',
  84. default=2,
  85. type=int,
  86. help='indentation width (defaults to 2 spaces)')
  87. group.add_argument(
  88. '--indent_after_first',
  89. dest='indent_after_first',
  90. action='store_true',
  91. default=False,
  92. help='indent after first line of statement (e.g. SELECT)')
  93. group.add_argument(
  94. '--indent_columns',
  95. dest='indent_columns',
  96. action='store_true',
  97. default=False,
  98. help='indent all columns by indent_width instead of keyword length')
  99. group.add_argument(
  100. '-a', '--reindent_aligned',
  101. action='store_true',
  102. default=False,
  103. help='reindent statements to aligned format')
  104. group.add_argument(
  105. '-s', '--use_space_around_operators',
  106. action='store_true',
  107. default=False,
  108. help='place spaces around mathematical operators')
  109. group.add_argument(
  110. '--wrap_after',
  111. dest='wrap_after',
  112. default=0,
  113. type=int,
  114. help='Column after which lists should be wrapped')
  115. group.add_argument(
  116. '--comma_first',
  117. dest='comma_first',
  118. default=False,
  119. type=bool,
  120. help='Insert linebreak before comma (default False)')
  121. group.add_argument(
  122. '--encoding',
  123. dest='encoding',
  124. default='utf-8',
  125. help='Specify the input encoding (default utf-8)')
  126. return parser
  127. def _error(msg):
  128. """Print msg and optionally exit with return code exit_."""
  129. sys.stderr.write(u'[ERROR] {0}\n'.format(msg))
  130. return 1
  131. def main(args=None):
  132. parser = create_parser()
  133. args = parser.parse_args(args)
  134. if args.filename == '-': # read from stdin
  135. if PY2:
  136. data = getreader(args.encoding)(sys.stdin).read()
  137. else:
  138. wrapper = TextIOWrapper(sys.stdin.buffer, encoding=args.encoding)
  139. try:
  140. data = wrapper.read()
  141. finally:
  142. wrapper.detach()
  143. else:
  144. try:
  145. with open(args.filename, 'r', args.encoding) as f:
  146. data = ''.join(f.readlines())
  147. except IOError as e:
  148. return _error(
  149. u'Failed to read {0}: {1}'.format(args.filename, e))
  150. close_stream = False
  151. if args.outfile:
  152. try:
  153. stream = open(args.outfile, 'w', args.encoding)
  154. close_stream = True
  155. except IOError as e:
  156. return _error(u'Failed to open {0}: {1}'.format(args.outfile, e))
  157. else:
  158. stream = sys.stdout
  159. formatter_opts = vars(args)
  160. try:
  161. formatter_opts = sqlparse.formatter.validate_options(formatter_opts)
  162. except SQLParseError as e:
  163. return _error(u'Invalid options: {0}'.format(e))
  164. s = sqlparse.format(data, **formatter_opts)
  165. stream.write(s)
  166. stream.flush()
  167. if close_stream:
  168. stream.close()
  169. return 0