197 lines
5.6 KiB
Python
197 lines
5.6 KiB
Python
|
#!/usr/bin/env python
|
||
|
#
|
||
|
# Copyright (C) 2009-2020 the sqlparse authors and contributors
|
||
|
# <see AUTHORS file>
|
||
|
#
|
||
|
# This module is part of python-sqlparse and is released under
|
||
|
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
|
||
|
|
||
|
"""Module that contains the command line app.
|
||
|
|
||
|
Why does this file exist, and why not put this in __main__?
|
||
|
You might be tempted to import things from __main__ later, but that will
|
||
|
cause problems: the code will get executed twice:
|
||
|
- When you run `python -m sqlparse` python will execute
|
||
|
``__main__.py`` as a script. That means there won't be any
|
||
|
``sqlparse.__main__`` in ``sys.modules``.
|
||
|
- When you import __main__ it will get executed again (as a module) because
|
||
|
there's no ``sqlparse.__main__`` in ``sys.modules``.
|
||
|
Also see (1) from http://click.pocoo.org/5/setuptools/#setuptools-integration
|
||
|
"""
|
||
|
|
||
|
import argparse
|
||
|
import sys
|
||
|
from io import TextIOWrapper
|
||
|
|
||
|
import sqlparse
|
||
|
from sqlparse.exceptions import SQLParseError
|
||
|
|
||
|
|
||
|
# TODO: Add CLI Tests
|
||
|
# TODO: Simplify formatter by using argparse `type` arguments
|
||
|
def create_parser():
|
||
|
_CASE_CHOICES = ['upper', 'lower', 'capitalize']
|
||
|
|
||
|
parser = argparse.ArgumentParser(
|
||
|
prog='sqlformat',
|
||
|
description='Format FILE according to OPTIONS. Use "-" as FILE '
|
||
|
'to read from stdin.',
|
||
|
usage='%(prog)s [OPTIONS] FILE, ...',
|
||
|
)
|
||
|
|
||
|
parser.add_argument('filename')
|
||
|
|
||
|
parser.add_argument(
|
||
|
'-o', '--outfile',
|
||
|
dest='outfile',
|
||
|
metavar='FILE',
|
||
|
help='write output to FILE (defaults to stdout)')
|
||
|
|
||
|
parser.add_argument(
|
||
|
'--version',
|
||
|
action='version',
|
||
|
version=sqlparse.__version__)
|
||
|
|
||
|
group = parser.add_argument_group('Formatting Options')
|
||
|
|
||
|
group.add_argument(
|
||
|
'-k', '--keywords',
|
||
|
metavar='CHOICE',
|
||
|
dest='keyword_case',
|
||
|
choices=_CASE_CHOICES,
|
||
|
help='change case of keywords, CHOICE is one of {}'.format(
|
||
|
', '.join('"{}"'.format(x) for x in _CASE_CHOICES)))
|
||
|
|
||
|
group.add_argument(
|
||
|
'-i', '--identifiers',
|
||
|
metavar='CHOICE',
|
||
|
dest='identifier_case',
|
||
|
choices=_CASE_CHOICES,
|
||
|
help='change case of identifiers, CHOICE is one of {}'.format(
|
||
|
', '.join('"{}"'.format(x) for x in _CASE_CHOICES)))
|
||
|
|
||
|
group.add_argument(
|
||
|
'-l', '--language',
|
||
|
metavar='LANG',
|
||
|
dest='output_format',
|
||
|
choices=['python', 'php'],
|
||
|
help='output a snippet in programming language LANG, '
|
||
|
'choices are "python", "php"')
|
||
|
|
||
|
group.add_argument(
|
||
|
'--strip-comments',
|
||
|
dest='strip_comments',
|
||
|
action='store_true',
|
||
|
default=False,
|
||
|
help='remove comments')
|
||
|
|
||
|
group.add_argument(
|
||
|
'-r', '--reindent',
|
||
|
dest='reindent',
|
||
|
action='store_true',
|
||
|
default=False,
|
||
|
help='reindent statements')
|
||
|
|
||
|
group.add_argument(
|
||
|
'--indent_width',
|
||
|
dest='indent_width',
|
||
|
default=2,
|
||
|
type=int,
|
||
|
help='indentation width (defaults to 2 spaces)')
|
||
|
|
||
|
group.add_argument(
|
||
|
'--indent_after_first',
|
||
|
dest='indent_after_first',
|
||
|
action='store_true',
|
||
|
default=False,
|
||
|
help='indent after first line of statement (e.g. SELECT)')
|
||
|
|
||
|
group.add_argument(
|
||
|
'--indent_columns',
|
||
|
dest='indent_columns',
|
||
|
action='store_true',
|
||
|
default=False,
|
||
|
help='indent all columns by indent_width instead of keyword length')
|
||
|
|
||
|
group.add_argument(
|
||
|
'-a', '--reindent_aligned',
|
||
|
action='store_true',
|
||
|
default=False,
|
||
|
help='reindent statements to aligned format')
|
||
|
|
||
|
group.add_argument(
|
||
|
'-s', '--use_space_around_operators',
|
||
|
action='store_true',
|
||
|
default=False,
|
||
|
help='place spaces around mathematical operators')
|
||
|
|
||
|
group.add_argument(
|
||
|
'--wrap_after',
|
||
|
dest='wrap_after',
|
||
|
default=0,
|
||
|
type=int,
|
||
|
help='Column after which lists should be wrapped')
|
||
|
|
||
|
group.add_argument(
|
||
|
'--comma_first',
|
||
|
dest='comma_first',
|
||
|
default=False,
|
||
|
type=bool,
|
||
|
help='Insert linebreak before comma (default False)')
|
||
|
|
||
|
group.add_argument(
|
||
|
'--encoding',
|
||
|
dest='encoding',
|
||
|
default='utf-8',
|
||
|
help='Specify the input encoding (default utf-8)')
|
||
|
|
||
|
return parser
|
||
|
|
||
|
|
||
|
def _error(msg):
|
||
|
"""Print msg and optionally exit with return code exit_."""
|
||
|
sys.stderr.write('[ERROR] {}\n'.format(msg))
|
||
|
return 1
|
||
|
|
||
|
|
||
|
def main(args=None):
|
||
|
parser = create_parser()
|
||
|
args = parser.parse_args(args)
|
||
|
|
||
|
if args.filename == '-': # read from stdin
|
||
|
wrapper = TextIOWrapper(sys.stdin.buffer, encoding=args.encoding)
|
||
|
try:
|
||
|
data = wrapper.read()
|
||
|
finally:
|
||
|
wrapper.detach()
|
||
|
else:
|
||
|
try:
|
||
|
with open(args.filename, encoding=args.encoding) as f:
|
||
|
data = ''.join(f.readlines())
|
||
|
except OSError as e:
|
||
|
return _error(
|
||
|
'Failed to read {}: {}'.format(args.filename, e))
|
||
|
|
||
|
close_stream = False
|
||
|
if args.outfile:
|
||
|
try:
|
||
|
stream = open(args.outfile, 'w', encoding=args.encoding)
|
||
|
close_stream = True
|
||
|
except OSError as e:
|
||
|
return _error('Failed to open {}: {}'.format(args.outfile, e))
|
||
|
else:
|
||
|
stream = sys.stdout
|
||
|
|
||
|
formatter_opts = vars(args)
|
||
|
try:
|
||
|
formatter_opts = sqlparse.formatter.validate_options(formatter_opts)
|
||
|
except SQLParseError as e:
|
||
|
return _error('Invalid options: {}'.format(e))
|
||
|
|
||
|
s = sqlparse.format(data, **formatter_opts)
|
||
|
stream.write(s)
|
||
|
stream.flush()
|
||
|
if close_stream:
|
||
|
stream.close()
|
||
|
return 0
|