def FormatFiles(filenames, lines, style_config=None, no_local_style=False, in_place=False, print_diff=False, verify=True): """Format a list of files. Arguments: filenames: (list of unicode) A list of files to reformat. lines: (list of tuples of integers) A list of tuples of lines, [start, end], that we want to format. The lines are 1-based indexed. This argument overrides the 'args.lines'. It can be used by third-party code (e.g., IDEs) when reformatting a snippet of code. style_config: (string) Style name or file path. no_local_style: (string) If style_config is None don't search for directory-local style configuration. in_place: (bool) Modify the files in place. print_diff: (bool) Instead of returning the reformatted source, return a diff that turns the formatted source into reformatter source. verify: (bool) True if reformatted code should be verified for syntax. Returns: True if the source code changed in any of the files being formatted. """ changed = False for filename in filenames: logging.info('Reformatting %s', filename) if style_config is None and not no_local_style: style_config = (file_resources.GetDefaultStyleForDir( os.path.dirname(filename))) try: reformatted_code, encoding, has_change = yapf_api.FormatFile( filename, in_place=in_place, style_config=style_config, lines=lines, print_diff=print_diff, verify=verify, logger=logging.warning) if has_change and reformatted_code is not None: file_resources.WriteReformattedCode(filename, reformatted_code, in_place, encoding) changed |= has_change except SyntaxError as e: e.filename = filename raise return changed
def print_help(args): """Prints the help menu.""" if args.style is None and not args.no_local_style: args.style = file_resources.GetDefaultStyleForDir(os.getcwd()) style.SetGlobalStyle(style.CreateStyleFromConfig(args.style)) print('[style]') for option, docstring in sorted(style.Help().items()): for line in docstring.splitlines(): print('#', line and ' ' or '', line, sep='') option_value = style.Get(option) if isinstance(option_value, set) or isinstance(option_value, list): option_value = ', '.join(map(str, option_value)) print(option.lower(), '=', option_value, sep='') print()
def fix_code(code): """Formats Python code to conform to the PEP 8 style guide. """ if not yapf_api: raise Fault('yapf not installed', code=400) style_config = file_resources.GetDefaultStyleForDir(os.getcwd()) try: reformatted_source, _ = yapf_api.FormatCode(code, filename='<stdin>', style_config=style_config, verify=False) return reformatted_source except Exception as e: raise Fault("Error during formatting: {}".format(e), code=400)
def _format(document, lines=None): new_source, changed = FormatCode( document.source, lines=lines, filename=document.filename, style_config=file_resources.GetDefaultStyleForDir( os.path.dirname(document.path) ) ) if not changed: return [] # I'm too lazy at the moment to parse diffs into TextEdit items # So let's just return the entire file... return [{ 'range': { 'start': {'line': 0, 'character': 0}, # End char 0 of the line after our document 'end': {'line': len(document.lines), 'character': 0} }, 'newText': new_source }]
def _FormatFile(filename, lines, style_config=None, no_local_style=False, in_place=False, print_diff=False, verify=False, quiet=False, verbose=False): """Format an individual file.""" if verbose and not quiet: print('Reformatting %s' % filename) if style_config is None and not no_local_style: style_config = file_resources.GetDefaultStyleForDir( os.path.dirname(filename)) try: reformatted_code, encoding, has_change = yapf_api.FormatFile( filename, in_place=in_place, style_config=style_config, lines=lines, print_diff=print_diff, verify=verify, logger=logging.warning) except tokenize.TokenError as e: raise errors.YapfError('%s:%s:%s' % (filename, e.args[1][0], e.args[0])) except SyntaxError as e: e.filename = filename raise if not in_place and not quiet and reformatted_code: file_resources.WriteReformattedCode(filename, reformatted_code, encoding, in_place) return has_change
def apply_callbacks(self, output_string, callbacks): """Apply callbacks.""" for callback in callbacks: if callback not in [x[0] for x in self.accepted["callbacks"]]: logger.error("Uknonwn callback %s", callback) continue if callback == "isort": import isort # pylint: disable=import-outside-toplevel output_string = isort.code(output_string) # pylint: disable=import-outside-toplevel if callback == "black": from black import format_str, FileMode # pylint: disable=import-outside-toplevel output_string = format_str(output_string, mode=FileMode()) if callback == "yapf": from yapf.yapflib.yapf_api import FormatCode # pylint: disable=import-outside-toplevel from yapf.yapflib import file_resources # pylint: disable=import-outside-toplevel style_config = file_resources.GetDefaultStyleForDir( os.getcwd()) output_string = FormatCode(output_string, style_config=style_config) # yapf<0.3 returns diff as str, >=0.3 returns a tuple of (diff, changed) output_string = output_string[0] if isinstance( output_string, tuple) else output_string return output_string
def main(argv): """Main program. Arguments: argv: command-line arguments, such as sys.argv (including the program name in argv[0]). Returns: 0 if there were no changes, non-zero otherwise. Raises: YapfError: if none of the supplied files were Python files. """ parser = argparse.ArgumentParser(description='Formatter for Python code.') parser.add_argument( '-v', '--version', action='store_true', help='show version number and exit') diff_inplace_group = parser.add_mutually_exclusive_group() diff_inplace_group.add_argument( '-d', '--diff', action='store_true', help='print the diff for the fixed source') diff_inplace_group.add_argument( '-i', '--in-place', action='store_true', help='make changes to files in place') lines_recursive_group = parser.add_mutually_exclusive_group() lines_recursive_group.add_argument( '-r', '--recursive', action='store_true', help='run recursively over directories') lines_recursive_group.add_argument( '-l', '--lines', metavar='START-END', action='append', default=None, help='range of lines to reformat, one-based') parser.add_argument( '-e', '--exclude', metavar='PATTERN', action='append', default=None, help='patterns for files to exclude from formatting') parser.add_argument( '--style', action='store', help=('specify formatting style: either a style name (for example "pep8" ' 'or "google"), or the name of a file with style settings. The ' 'default is pep8 unless a %s or %s file located in one of the ' 'parent directories of the source file (or current directory for ' 'stdin)' % (style.LOCAL_STYLE, style.SETUP_CONFIG))) parser.add_argument( '--style-help', action='store_true', help=('show style settings and exit; this output can be ' 'saved to .style.yapf to make your settings ' 'permanent')) parser.add_argument( '--no-local-style', action='store_true', help="don't search for local style definition") parser.add_argument('--verify', action='store_true', help=argparse.SUPPRESS) parser.add_argument( '-p', '--parallel', action='store_true', help=('Run yapf in parallel when formatting multiple files. Requires ' 'concurrent.futures in Python 2.X')) parser.add_argument('files', nargs='*') args = parser.parse_args(argv[1:]) if args.version: print('yapf {}'.format(__version__)) return 0 if args.style_help: style.SetGlobalStyle(style.CreateStyleFromConfig(args.style)) print('[style]') for option, docstring in sorted(style.Help().items()): for line in docstring.splitlines(): print('#', line and ' ' or '', line, sep='') print(option.lower(), '=', style.Get(option), sep='') print() return 0 if args.lines and len(args.files) > 1: parser.error('cannot use -l/--lines with more than one file') lines = _GetLines(args.lines) if args.lines is not None else None if not args.files: # No arguments specified. Read code from stdin. if args.in_place or args.diff: parser.error('cannot use --in-place or --diff flags when reading ' 'from stdin') original_source = [] while True: try: # Use 'raw_input' instead of 'sys.stdin.read', because otherwise the # user will need to hit 'Ctrl-D' more than once if they're inputting # the program by hand. 'raw_input' throws an EOFError exception if # 'Ctrl-D' is pressed, which makes it easy to bail out of this loop. original_source.append(py3compat.raw_input()) except EOFError: break style_config = args.style if style_config is None and not args.no_local_style: style_config = file_resources.GetDefaultStyleForDir(os.getcwd()) source = [line.rstrip() for line in original_source] reformatted_source, _ = yapf_api.FormatCode( py3compat.unicode('\n'.join(source) + '\n'), filename='<stdin>', style_config=style_config, lines=lines, verify=args.verify) file_resources.WriteReformattedCode('<stdout>', reformatted_source) return 0 files = file_resources.GetCommandLineFiles(args.files, args.recursive, args.exclude) if not files: raise errors.YapfError('Input filenames did not match any python files') FormatFiles( files, lines, style_config=args.style, no_local_style=args.no_local_style, in_place=args.in_place, print_diff=args.diff, verify=args.verify, parallel=args.parallel) return 0
def main(argv): """Main program. Arguments: argv: command-line arguments, such as sys.argv (including the program name in argv[0]). Returns: Zero on successful program termination, non-zero otherwise. With --diff: zero if there were no changes, non-zero otherwise. Raises: YapfError: if none of the supplied files were Python files. """ args = _ParseArguments(argv) if args.version: print('yapf {}'.format(__version__)) return 0 style_config = args.style if args.style_help: print_help(args) return 0 if args.lines and len(args.files) > 1: parser.error('cannot use -l/--lines with more than one file') lines = _GetLines(args.lines) if args.lines is not None else None if not args.files: # No arguments specified. Read code from stdin. if args.in_place or args.diff: parser.error('cannot use --in-place or --diff flags when reading ' 'from stdin') original_source = [] while True: if sys.stdin.closed: break try: # Use 'raw_input' instead of 'sys.stdin.read', because otherwise the # user will need to hit 'Ctrl-D' more than once if they're inputting # the program by hand. 'raw_input' throws an EOFError exception if # 'Ctrl-D' is pressed, which makes it easy to bail out of this loop. original_source.append(py3compat.raw_input()) except EOFError: break except KeyboardInterrupt: return 1 if style_config is None and not args.no_local_style: style_config = file_resources.GetDefaultStyleForDir(os.getcwd()) source = [line.rstrip() for line in original_source] source[0] = py3compat.removeBOM(source[0]) try: reformatted_source, _ = yapf_api.FormatCode( py3compat.unicode('\n'.join(source) + '\n'), filename='<stdin>', style_config=style_config, lines=lines, verify=args.verify) except tokenize.TokenError as e: raise errors.YapfError('%s:%s' % (e.args[1][0], e.args[0])) file_resources.WriteReformattedCode('<stdout>', reformatted_source) return 0 # Get additional exclude patterns from ignorefile exclude_patterns_from_ignore_file = file_resources.GetExcludePatternsForDir( os.getcwd()) files = file_resources.GetCommandLineFiles( args.files, args.recursive, (args.exclude or []) + exclude_patterns_from_ignore_file) if not files: raise errors.YapfError( 'Input filenames did not match any python files') changed = FormatFiles(files, lines, style_config=args.style, no_local_style=args.no_local_style, in_place=args.in_place, print_diff=args.diff, verify=args.verify, parallel=args.parallel, quiet=args.quiet, verbose=args.verbose) return 1 if changed and (args.diff or args.quiet) else 0
def test_no_local_style_custom_default(self): test_file = os.path.join(self.test_tmpdir, 'file.py') style_name = file_resources.GetDefaultStyleForDir( test_file, default_style='custom-default') self.assertEqual(style_name, 'custom-default')
def test_no_local_style(self): test_file = os.path.join(self.test_tmpdir, 'file.py') style_name = file_resources.GetDefaultStyleForDir(test_file) self.assertEqual(style_name, 'pep8')
def main(argv): """Main program. Arguments: argv: command-line arguments, such as sys.argv (including the program name in argv[0]). Returns: Zero on successful program termination, non-zero otherwise. With --diff: zero if there were no changes, non-zero otherwise. Raises: YapfError: if none of the supplied files were Python files. """ parser = argparse.ArgumentParser(description='Formatter for Python code.') parser.add_argument( '-v', '--version', action='store_true', help='show version number and exit') diff_inplace_quiet_group = parser.add_mutually_exclusive_group() diff_inplace_quiet_group.add_argument( '-d', '--diff', action='store_true', help='print the diff for the fixed source') diff_inplace_quiet_group.add_argument( '-i', '--in-place', action='store_true', help='make changes to files in place') diff_inplace_quiet_group.add_argument( '-q', '--quiet', action='store_true', help='output nothing and set return value') lines_recursive_group = parser.add_mutually_exclusive_group() lines_recursive_group.add_argument( '-r', '--recursive', action='store_true', help='run recursively over directories') lines_recursive_group.add_argument( '-l', '--lines', metavar='START-END', action='append', default=None, help='range of lines to reformat, one-based') parser.add_argument( '-e', '--exclude', metavar='PATTERN', action='append', default=None, help='patterns for files to exclude from formatting') parser.add_argument( '--style', action='store', help=('specify formatting style: either a style name (for example "pep8" ' 'or "google"), or the name of a file with style settings. The ' 'default is pep8 unless a %s or %s file located in the same ' 'directory as the source or one of its parent directories ' '(for stdin, the current directory is used).' % (style.LOCAL_STYLE, style.SETUP_CONFIG))) parser.add_argument( '--style-help', action='store_true', help=('show style settings and exit; this output can be ' 'saved to .style.yapf to make your settings ' 'permanent')) parser.add_argument( '--no-local-style', action='store_true', help="don't search for local style definition") parser.add_argument('--verify', action='store_true', help=argparse.SUPPRESS) parser.add_argument( '-p', '--parallel', action='store_true', help=('run yapf in parallel when formatting multiple files. Requires ' 'concurrent.futures in Python 2.X')) parser.add_argument( '-vv', '--verbose', action='store_true', help='print out file names while processing') parser.add_argument( 'files', nargs='*', help='reads from stdin when no files are specified.') args = parser.parse_args(argv[1:]) if args.version: print('yapf {}'.format(__version__)) return 0 style_config = args.style if args.style_help: print_help(args) return 0 if args.lines and len(args.files) > 1: parser.error('cannot use -l/--lines with more than one file') lines = _GetLines(args.lines) if args.lines is not None else None if not args.files: # No arguments specified. Read code from stdin. if args.in_place or args.diff: parser.error('cannot use --in-place or --diff flags when reading ' 'from stdin') original_source = [] while True: if sys.stdin.closed: break try: # Use 'raw_input' instead of 'sys.stdin.read', because otherwise the # user will need to hit 'Ctrl-D' more than once if they're inputting # the program by hand. 'raw_input' throws an EOFError exception if # 'Ctrl-D' is pressed, which makes it easy to bail out of this loop. original_source.append(py3compat.raw_input()) except EOFError: break except KeyboardInterrupt: return 1 if style_config is None and not args.no_local_style: style_config = file_resources.GetDefaultStyleForDir(os.getcwd()) source = [line.rstrip() for line in original_source] source[0] = py3compat.removeBOM(source[0]) try: reformatted_source, _ = yapf_api.FormatCode( py3compat.unicode('\n'.join(source) + '\n'), filename='<stdin>', style_config=style_config, lines=lines, verify=args.verify) except tokenize.TokenError as e: raise errors.YapfError('%s:%s' % (e.args[1][0], e.args[0])) file_resources.WriteReformattedCode('<stdout>', reformatted_source) return 0 # Get additional exclude patterns from ignorefile exclude_patterns_from_ignore_file = file_resources.GetExcludePatternsForDir( os.getcwd()) files = file_resources.GetCommandLineFiles(args.files, args.recursive, (args.exclude or []) + exclude_patterns_from_ignore_file) if not files: raise errors.YapfError('Input filenames did not match any python files') changed = FormatFiles( files, lines, style_config=args.style, no_local_style=args.no_local_style, in_place=args.in_place, print_diff=args.diff, verify=args.verify, parallel=args.parallel, quiet=args.quiet, verbose=args.verbose) return 1 if changed and (args.diff or args.quiet) else 0
def _get_file_style(): path = vim.current.buffer.name or os.getcwd() project_style = file_resources.GetDefaultStyleForDir(path) if project_style == style.DEFAULT_STYLE: return None return project_style
def __init__(self, path, parent): super(YapfItem, self).__init__(path, parent) self.path = str(path) self.show_diff = self.parent.config.option.yapfdiff is True self.style = self.parent.config.getoption( 'yapfstyle') or file_resources.GetDefaultStyleForDir(self.path)
def rpc_lsp_formatter_yapf(self, args: List[Any]) -> Any: buf_path: Optional[str] buf_root_dir: str default_config_path: str fmt_ranges: Optional[List[List[int]]] buf_lines: List[str] buf_path, buf_root_dir, default_config_path, fmt_ranges, buf_lines = args try: from yapf.yapflib.yapf_api import FormatCode from yapf.yapflib import file_resources from yapf.yapflib.errors import YapfError from lib2to3.pgen2.parse import ParseError as ParseError2to3 except ModuleNotFoundError as err: raise pynvim.ErrorResponse('yapf is not installed: {}'.format(err)) # The following code is essentially a reimplementation of # <https://github.com/google/yapf/blob/v0.31.0/yapf/__init__.py#L82-L114>. try: buf_dir = os.path.dirname( buf_path) if buf_path is not None else buf_root_dir config_path = default_config_path if buf_dir is not None: # This thing is actually not possible to pull off just through shelling # out to Yapf because it only has an option to override the config # globally and without any regard for project-local settings. config_path = file_resources.GetDefaultStyleForDir( buf_dir, default_config_path) if buf_root_dir is not None and buf_path is not None: # It should be mentioned that this function doesn't look for files in # parent directories, which is a shame. excluded_patterns = file_resources.GetExcludePatternsForDir( buf_root_dir) if buf_path.startswith(buf_root_dir): buf_path = buf_path[len(buf_root_dir):] if file_resources.IsIgnored(buf_path, excluded_patterns): return None # TODO: comment here about normalization of newlines by yapf and how a # string takes up less space than an array of them when encoded also how # Vim handles BOM buf_text = '\n'.join(buf_lines) + '\n' try: fmt_text, changed = FormatCode( buf_text, filename=buf_path if buf_path is not None else '<unknown>', style_config=config_path, lines=fmt_ranges, verify=False) except ParseError2to3 as err: # lineno, offset = err.context[1] # raise pynvim.ErrorResponse( # 'yapf: syntax error on {}:{}: {}'.format(lineno, offset, err.msg) # ) return None except SyntaxError as err: # raise pynvim.ErrorResponse( # 'yapf: syntax error on {}:{}: {}'.format(err.lineno, err.offset, err.msg) # ) return None if not changed: return None # TODO: write a continuation of that comment here as well fmt_lines = (fmt_text[:-1] if fmt_text.endswith('\n') else fmt_text).split('\n') changed, common_lines_from_start, common_lines_from_end = ( dotfiles.utils.simple_line_diff(buf_lines, fmt_lines)) if not changed: return None if common_lines_from_start > 0: fmt_lines = fmt_lines[common_lines_from_start:] if common_lines_from_end > 0: fmt_lines = fmt_lines[:-common_lines_from_end] return (common_lines_from_start, common_lines_from_end, fmt_lines) except YapfError as err: # <https://github.com/google/yapf/blob/5fda04e1cdf50f548e121173337e07cc5304b752/yapf/__init__.py#L363-L365> # raise pynvim.ErrorResponse('yapf: {}'.format(err)) return None