Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlparse/cli.py: 12%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (C) 2009-2020 the sqlparse authors and contributors
2# <see AUTHORS file>
3#
4# This module is part of python-sqlparse and is released under
5# the BSD License: https://opensource.org/licenses/BSD-3-Clause
7"""Module that contains the command line app.
9Why does this file exist, and why not put this in __main__?
10 You might be tempted to import things from __main__ later, but that will
11 cause problems: the code will get executed twice:
12 - When you run `python -m sqlparse` python will execute
13 ``__main__.py`` as a script. That means there won't be any
14 ``sqlparse.__main__`` in ``sys.modules``.
15 - When you import __main__ it will get executed again (as a module) because
16 there's no ``sqlparse.__main__`` in ``sys.modules``.
17 Also see (1) from http://click.pocoo.org/5/setuptools/#setuptools-integration
18"""
20import argparse
21import sys
22from io import TextIOWrapper
24import sqlparse
25from sqlparse.exceptions import SQLParseError
28# TODO: Add CLI Tests
29# TODO: Simplify formatter by using argparse `type` arguments
30def create_parser():
31 _CASE_CHOICES = ['upper', 'lower', 'capitalize']
33 parser = argparse.ArgumentParser(
34 prog='sqlformat',
35 description='Format FILE according to OPTIONS. Use "-" as FILE '
36 'to read from stdin.',
37 usage='%(prog)s [OPTIONS] FILE [FILE ...]',
38 )
40 parser.add_argument(
41 'filename',
42 nargs='+',
43 help='file(s) to format (use "-" for stdin)')
45 parser.add_argument(
46 '-o', '--outfile',
47 dest='outfile',
48 metavar='FILE',
49 help='write output to FILE (defaults to stdout)')
51 parser.add_argument(
52 '--in-place',
53 dest='inplace',
54 action='store_true',
55 default=False,
56 help='format files in-place (overwrite existing files)')
58 parser.add_argument(
59 '--version',
60 action='version',
61 version=sqlparse.__version__)
63 group = parser.add_argument_group('Formatting Options')
65 group.add_argument(
66 '-k', '--keywords',
67 metavar='CHOICE',
68 dest='keyword_case',
69 choices=_CASE_CHOICES,
70 help='change case of keywords, CHOICE is one of {}'.format(
71 ', '.join(f'"{x}"' for x in _CASE_CHOICES)))
73 group.add_argument(
74 '-i', '--identifiers',
75 metavar='CHOICE',
76 dest='identifier_case',
77 choices=_CASE_CHOICES,
78 help='change case of identifiers, CHOICE is one of {}'.format(
79 ', '.join(f'"{x}"' for x in _CASE_CHOICES)))
81 group.add_argument(
82 '-l', '--language',
83 metavar='LANG',
84 dest='output_format',
85 choices=['python', 'php'],
86 help='output a snippet in programming language LANG, '
87 'choices are "python", "php"')
89 group.add_argument(
90 '--strip-comments',
91 dest='strip_comments',
92 action='store_true',
93 default=False,
94 help='remove comments')
96 group.add_argument(
97 '-r', '--reindent',
98 dest='reindent',
99 action='store_true',
100 default=False,
101 help='reindent statements')
103 group.add_argument(
104 '--indent_width',
105 dest='indent_width',
106 default=2,
107 type=int,
108 help='indentation width (defaults to 2 spaces)')
110 group.add_argument(
111 '--indent_after_first',
112 dest='indent_after_first',
113 action='store_true',
114 default=False,
115 help='indent after first line of statement (e.g. SELECT)')
117 group.add_argument(
118 '--indent_columns',
119 dest='indent_columns',
120 action='store_true',
121 default=False,
122 help='indent all columns by indent_width instead of keyword length')
124 group.add_argument(
125 '-a', '--reindent_aligned',
126 action='store_true',
127 default=False,
128 help='reindent statements to aligned format')
130 group.add_argument(
131 '-s', '--use_space_around_operators',
132 action='store_true',
133 default=False,
134 help='place spaces around mathematical operators')
136 group.add_argument(
137 '--wrap_after',
138 dest='wrap_after',
139 default=0,
140 type=int,
141 help='Column after which lists should be wrapped')
143 group.add_argument(
144 '--comma_first',
145 dest='comma_first',
146 default=False,
147 type=bool,
148 help='Insert linebreak before comma (default False)')
150 group.add_argument(
151 '--compact',
152 dest='compact',
153 default=False,
154 type=bool,
155 help='Try to produce more compact output (default False)')
157 group.add_argument(
158 '--encoding',
159 dest='encoding',
160 default='utf-8',
161 help='Specify the input encoding (default utf-8)')
163 return parser
166def _error(msg):
167 """Print msg and optionally exit with return code exit_."""
168 sys.stderr.write(f'[ERROR] {msg}\n')
169 return 1
172def _process_file(filename, args):
173 """Process a single file with the given formatting options.
175 Returns 0 on success, 1 on error.
176 """
177 # Check for incompatible option combinations first
178 if filename == '-' and args.inplace:
179 return _error('Cannot use --in-place with stdin')
181 # Read input
182 if filename == '-': # read from stdin
183 wrapper = TextIOWrapper(sys.stdin.buffer, encoding=args.encoding)
184 try:
185 data = wrapper.read()
186 finally:
187 wrapper.detach()
188 else:
189 try:
190 with open(filename, encoding=args.encoding) as f:
191 data = ''.join(f.readlines())
192 except OSError as e:
193 return _error(f'Failed to read {filename}: {e}')
195 # Determine output destination
196 close_stream = False
197 if args.inplace:
198 try:
199 stream = open(filename, 'w', encoding=args.encoding)
200 close_stream = True
201 except OSError as e:
202 return _error(f'Failed to open {filename}: {e}')
203 elif args.outfile:
204 try:
205 stream = open(args.outfile, 'w', encoding=args.encoding)
206 close_stream = True
207 except OSError as e:
208 return _error(f'Failed to open {args.outfile}: {e}')
209 else:
210 stream = sys.stdout
212 # Format the SQL
213 formatter_opts = vars(args)
214 try:
215 formatter_opts = sqlparse.formatter.validate_options(formatter_opts)
216 except SQLParseError as e:
217 return _error(f'Invalid options: {e}')
219 s = sqlparse.format(data, **formatter_opts)
220 stream.write(s)
221 stream.flush()
222 if close_stream:
223 stream.close()
224 return 0
227def main(args=None):
228 parser = create_parser()
229 args = parser.parse_args(args)
231 # Validate argument combinations
232 if len(args.filename) > 1:
233 if args.outfile:
234 return _error('Cannot use -o/--outfile with multiple files')
235 if not args.inplace:
236 return _error('Multiple files require --in-place flag')
238 # Process all files
239 exit_code = 0
240 for filename in args.filename:
241 result = _process_file(filename, args)
242 if result != 0:
243 exit_code = result
244 # Continue processing remaining files even if one fails
246 return exit_code