]> crepu.dev Git - config.git/blame - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/yapf/__init__.py
ActualizaciĆ³n de Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / yapf / __init__.py
CommitLineData
53e6db90
DC
1# Copyright 2015 Google Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""YAPF.
15
16YAPF uses the algorithm in clang-format to figure out the "best" formatting for
17Python code. It looks at the program as a series of "unwrappable lines" ---
18i.e., lines which, if there were no column limit, we would place all tokens on
19that line. It then uses a priority queue to figure out what the best formatting
20is --- i.e., the formatting with the least penalty.
21
22It differs from tools like autopep8 in that it doesn't just look for
23violations of the style guide, but looks at the module as a whole, making
24formatting decisions based on what's the best format for each line.
25
26If no filenames are specified, YAPF reads the code from stdin.
27"""
28
29import argparse
30import codecs
31import io
32import logging
33import os
34import sys
35
36from importlib_metadata import metadata
37
38from yapf.yapflib import errors
39from yapf.yapflib import file_resources
40from yapf.yapflib import style
41from yapf.yapflib import yapf_api
42
43__version__ = metadata('yapf')['Version']
44
45
46def _raw_input():
47 wrapper = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
48 return wrapper.buffer.raw.readall().decode('utf-8')
49
50
51def _removeBOM(source):
52 """Remove any Byte-order-Mark bytes from the beginning of a file."""
53 bom = codecs.BOM_UTF8
54 bom = bom.decode('utf-8')
55 if source.startswith(bom):
56 return source[len(bom):]
57 return source
58
59
60def main(argv):
61 """Main program.
62
63 Arguments:
64 argv: command-line arguments, such as sys.argv (including the program name
65 in argv[0]).
66
67 Returns:
68 Zero on successful program termination, non-zero otherwise.
69 With --diff: zero if there were no changes, non-zero otherwise.
70
71 Raises:
72 YapfError: if none of the supplied files were Python files.
73 """
74 parser = _BuildParser()
75 args = parser.parse_args(argv[1:])
76 style_config = args.style
77
78 if args.style_help:
79 _PrintHelp(args)
80 return 0
81
82 if args.lines and len(args.files) > 1:
83 parser.error('cannot use -l/--lines with more than one file')
84
85 lines = _GetLines(args.lines) if args.lines is not None else None
86 if not args.files:
87 # No arguments specified. Read code from stdin.
88 if args.in_place or args.diff:
89 parser.error('cannot use --in-place or --diff flags when reading '
90 'from stdin')
91
92 original_source = []
93 while True:
94 # Test that sys.stdin has the "closed" attribute. When using pytest, it
95 # co-opts sys.stdin, which makes the "main_tests.py" fail. This is gross.
96 if hasattr(sys.stdin, 'closed') and sys.stdin.closed:
97 break
98 try:
99 # Use 'raw_input' instead of 'sys.stdin.read', because otherwise the
100 # user will need to hit 'Ctrl-D' more than once if they're inputting
101 # the program by hand. 'raw_input' throws an EOFError exception if
102 # 'Ctrl-D' is pressed, which makes it easy to bail out of this loop.
103 original_source.append(_raw_input())
104 except EOFError:
105 break
106 except KeyboardInterrupt:
107 return 1
108
109 if style_config is None and not args.no_local_style:
110 style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
111
112 source = [line.rstrip() for line in original_source]
113 source[0] = _removeBOM(source[0])
114
115 try:
116 reformatted_source, _ = yapf_api.FormatCode(
117 str('\n'.join(source).replace('\r\n', '\n') + '\n'),
118 filename='<stdin>',
119 style_config=style_config,
120 lines=lines)
121 except errors.YapfError:
122 raise
123 except Exception as e:
124 raise errors.YapfError(errors.FormatErrorMsg(e))
125
126 file_resources.WriteReformattedCode('<stdout>', reformatted_source)
127 return 0
128
129 # Get additional exclude patterns from ignorefile
130 exclude_patterns_from_ignore_file = file_resources.GetExcludePatternsForDir(
131 os.getcwd())
132
133 files = file_resources.GetCommandLineFiles(args.files, args.recursive,
134 (args.exclude or []) +
135 exclude_patterns_from_ignore_file)
136 if not files:
137 raise errors.YapfError('input filenames did not match any python files')
138
139 changed = FormatFiles(
140 files,
141 lines,
142 style_config=args.style,
143 no_local_style=args.no_local_style,
144 in_place=args.in_place,
145 print_diff=args.diff,
146 parallel=args.parallel,
147 quiet=args.quiet,
148 verbose=args.verbose,
149 print_modified=args.print_modified)
150 return 1 if changed and (args.diff or args.quiet) else 0
151
152
153def _PrintHelp(args):
154 """Prints the help menu."""
155
156 if args.style is None and not args.no_local_style:
157 args.style = file_resources.GetDefaultStyleForDir(os.getcwd())
158 style.SetGlobalStyle(style.CreateStyleFromConfig(args.style))
159 print('[style]')
160 for option, docstring in sorted(style.Help().items()):
161 for line in docstring.splitlines():
162 print('#', line and ' ' or '', line, sep='')
163 option_value = style.Get(option)
164 if isinstance(option_value, (set, list)):
165 option_value = ', '.join(map(str, option_value))
166 print(option.lower(), '=', option_value, sep='')
167 print()
168
169
170def FormatFiles(filenames,
171 lines,
172 style_config=None,
173 no_local_style=False,
174 in_place=False,
175 print_diff=False,
176 parallel=False,
177 quiet=False,
178 verbose=False,
179 print_modified=False):
180 """Format a list of files.
181
182 Arguments:
183 filenames: (list of unicode) A list of files to reformat.
184 lines: (list of tuples of integers) A list of tuples of lines, [start, end],
185 that we want to format. The lines are 1-based indexed. This argument
186 overrides the 'args.lines'. It can be used by third-party code (e.g.,
187 IDEs) when reformatting a snippet of code.
188 style_config: (string) Style name or file path.
189 no_local_style: (string) If style_config is None don't search for
190 directory-local style configuration.
191 in_place: (bool) Modify the files in place.
192 print_diff: (bool) Instead of returning the reformatted source, return a
193 diff that turns the formatted source into reformatter source.
194 parallel: (bool) True if should format multiple files in parallel.
195 quiet: (bool) True if should output nothing.
196 verbose: (bool) True if should print out filenames while processing.
197 print_modified: (bool) True if should print out filenames of modified files.
198
199 Returns:
200 True if the source code changed in any of the files being formatted.
201 """
202 changed = False
203 if parallel:
204 import concurrent.futures # pylint: disable=g-import-not-at-top
205 import multiprocessing # pylint: disable=g-import-not-at-top
206 workers = min(multiprocessing.cpu_count(), len(filenames))
207 with concurrent.futures.ProcessPoolExecutor(workers) as executor:
208 future_formats = [
209 executor.submit(_FormatFile, filename, lines, style_config,
210 no_local_style, in_place, print_diff, quiet, verbose,
211 print_modified) for filename in filenames
212 ]
213 for future in concurrent.futures.as_completed(future_formats):
214 changed |= future.result()
215 else:
216 for filename in filenames:
217 changed |= _FormatFile(filename, lines, style_config, no_local_style,
218 in_place, print_diff, quiet, verbose,
219 print_modified)
220 return changed
221
222
223def _FormatFile(filename,
224 lines,
225 style_config=None,
226 no_local_style=False,
227 in_place=False,
228 print_diff=False,
229 quiet=False,
230 verbose=False,
231 print_modified=False):
232 """Format an individual file."""
233 if verbose and not quiet:
234 print(f'Reformatting {filename}')
235
236 if style_config is None and not no_local_style:
237 style_config = file_resources.GetDefaultStyleForDir(
238 os.path.dirname(filename))
239
240 try:
241 reformatted_code, encoding, has_change = yapf_api.FormatFile(
242 filename,
243 in_place=in_place,
244 style_config=style_config,
245 lines=lines,
246 print_diff=print_diff,
247 logger=logging.warning)
248 except errors.YapfError:
249 raise
250 except Exception as e:
251 raise errors.YapfError(errors.FormatErrorMsg(e))
252
253 if not in_place and not quiet and reformatted_code:
254 file_resources.WriteReformattedCode(filename, reformatted_code, encoding,
255 in_place)
256 if print_modified and has_change and in_place and not quiet:
257 print(f'Formatted {filename}')
258 return has_change
259
260
261def _GetLines(line_strings):
262 """Parses the start and end lines from a line string like 'start-end'.
263
264 Arguments:
265 line_strings: (array of string) A list of strings representing a line
266 range like 'start-end'.
267
268 Returns:
269 A list of tuples of the start and end line numbers.
270
271 Raises:
272 ValueError: If the line string failed to parse or was an invalid line range.
273 """
274 lines = []
275 for line_string in line_strings:
276 # The 'list' here is needed by Python 3.
277 line = list(map(int, line_string.split('-', 1)))
278 if line[0] < 1:
279 raise errors.YapfError('invalid start of line range: %r' % line)
280 if line[0] > line[1]:
281 raise errors.YapfError('end comes before start in line range: %r' % line)
282 lines.append(tuple(line))
283 return lines
284
285
286def _BuildParser():
287 """Constructs the parser for the command line arguments.
288
289 Returns:
290 An ArgumentParser instance for the CLI.
291 """
292 parser = argparse.ArgumentParser(
293 prog='yapf', description='Formatter for Python code.')
294 parser.add_argument(
295 '-v',
296 '--version',
297 action='version',
298 version='%(prog)s {}'.format(__version__))
299
300 diff_inplace_quiet_group = parser.add_mutually_exclusive_group()
301 diff_inplace_quiet_group.add_argument(
302 '-d',
303 '--diff',
304 action='store_true',
305 help='print the diff for the fixed source')
306 diff_inplace_quiet_group.add_argument(
307 '-i',
308 '--in-place',
309 action='store_true',
310 help='make changes to files in place')
311 diff_inplace_quiet_group.add_argument(
312 '-q',
313 '--quiet',
314 action='store_true',
315 help='output nothing and set return value')
316
317 lines_recursive_group = parser.add_mutually_exclusive_group()
318 lines_recursive_group.add_argument(
319 '-r',
320 '--recursive',
321 action='store_true',
322 help='run recursively over directories')
323 lines_recursive_group.add_argument(
324 '-l',
325 '--lines',
326 metavar='START-END',
327 action='append',
328 default=None,
329 help='range of lines to reformat, one-based')
330
331 parser.add_argument(
332 '-e',
333 '--exclude',
334 metavar='PATTERN',
335 action='append',
336 default=None,
337 help='patterns for files to exclude from formatting')
338 parser.add_argument(
339 '--style',
340 action='store',
341 help=('specify formatting style: either a style name (for example "pep8" '
342 'or "google"), or the name of a file with style settings. The '
343 'default is pep8 unless a %s or %s or %s file located in the same '
344 'directory as the source or one of its parent directories '
345 '(for stdin, the current directory is used).' %
346 (style.LOCAL_STYLE, style.SETUP_CONFIG, style.PYPROJECT_TOML)))
347 parser.add_argument(
348 '--style-help',
349 action='store_true',
350 help=('show style settings and exit; this output can be '
351 'saved to .style.yapf to make your settings '
352 'permanent'))
353 parser.add_argument(
354 '--no-local-style',
355 action='store_true',
356 help="don't search for local style definition")
357 parser.add_argument(
358 '-p',
359 '--parallel',
360 action='store_true',
361 help=('run YAPF in parallel when formatting multiple files.'))
362 parser.add_argument(
363 '-m',
364 '--print-modified',
365 action='store_true',
366 help='print out file names of modified files')
367 parser.add_argument(
368 '-vv',
369 '--verbose',
370 action='store_true',
371 help='print out file names while processing')
372
373 parser.add_argument(
374 'files', nargs='*', help='reads from stdin when no files are specified.')
375 return parser
376
377
378def run_main(): # pylint: disable=invalid-name
379 try:
380 sys.exit(main(sys.argv))
381 except errors.YapfError as e:
382 sys.stderr.write('yapf: ' + str(e) + '\n')
383 sys.exit(1)
384
385
386if __name__ == '__main__':
387 run_main()