def reformat_code(content: str, fast: bool, write_back: WriteBack, mode: Mode, report: Report) -> None: """ Reformat and print out `content` without spawning child processes. Similar to `reformat_one`, but for string content. `fast`, `write_back`, and `mode` options are passed to :func:`format_file_in_place` or :func:`format_stdin_to_stdout`. """ path = Path("<string>") try: changed = Changed.NO if format_stdin_to_stdout(content=content, fast=fast, write_back=write_back, mode=mode): changed = Changed.YES report.done(path, changed) except Exception as exc: if report.verbose: traceback.print_exc() report.failed(path, str(exc))
def normalize_path_maybe_ignore(path: Path, root: Path, report: Report) -> Optional[str]: """Normalize `path`. May return `None` if `path` was ignored. `report` is where "path ignored" output goes. """ try: abspath = path if path.is_absolute() else Path.cwd() / path normalized_path = abspath.resolve().relative_to(root).as_posix() except OSError as e: report.path_ignored(path, f"cannot be read because {e}") return None except ValueError: if path.is_symlink(): report.path_ignored( path, f"is a symbolic link that points outside {root}") return None raise return normalized_path
def gen_python_files( paths: Iterable[Path], root: Path, include: Pattern[str], exclude: Pattern[str], extend_exclude: Optional[Pattern[str]], force_exclude: Optional[Pattern[str]], report: Report, gitignore: Optional[PathSpec], *, verbose: bool, quiet: bool, ) -> Iterator[Path]: """Generate all files under `path` whose paths are not excluded by the `exclude_regex`, `extend_exclude`, or `force_exclude` regexes, but are included by the `include` regex. Symbolic links pointing outside of the `root` directory are ignored. `report` is where output about exclusions goes. """ assert root.is_absolute( ), f"INTERNAL ERROR: `root` must be absolute but is {root}" for child in paths: normalized_path = normalize_path_maybe_ignore(child, root, report) if normalized_path is None: continue # First ignore files matching .gitignore, if passed if gitignore is not None and gitignore.match_file(normalized_path): report.path_ignored(child, "matches the .gitignore file content") continue # Then ignore with `--exclude` `--extend-exclude` and `--force-exclude` options. normalized_path = "/" + normalized_path if child.is_dir(): normalized_path += "/" if path_is_excluded(normalized_path, exclude): report.path_ignored(child, "matches the --exclude regular expression") continue if path_is_excluded(normalized_path, extend_exclude): report.path_ignored( child, "matches the --extend-exclude regular expression") continue if path_is_excluded(normalized_path, force_exclude): report.path_ignored( child, "matches the --force-exclude regular expression") continue if child.is_dir(): # If gitignore is None, gitignore usage is disabled, while a Falsey # gitignore is when the directory doesn't have a .gitignore file. yield from gen_python_files( child.iterdir(), root, include, exclude, extend_exclude, force_exclude, report, gitignore + get_gitignore(child) if gitignore is not None else None, verbose=verbose, quiet=quiet, ) elif child.is_file(): if child.suffix == ".ipynb" and not jupyter_dependencies_are_installed( verbose=verbose, quiet=quiet): continue include_match = include.search( normalized_path) if include else True if include_match: yield child
def main( ctx: click.Context, code: Optional[str], line_length: int, target_version: List[TargetVersion], check: bool, diff: bool, color: bool, fast: bool, pyi: bool, ipynb: bool, skip_string_normalization: bool, skip_magic_trailing_comma: bool, experimental_string_processing: bool, quiet: bool, verbose: bool, required_version: Optional[str], include: Pattern[str], exclude: Optional[Pattern[str]], extend_exclude: Optional[Pattern[str]], force_exclude: Optional[Pattern[str]], stdin_filename: Optional[str], workers: int, src: Tuple[str, ...], config: Optional[str], ) -> None: """The uncompromising code formatter.""" if config and verbose: out(f"Using configuration from {config}.", bold=False, fg="blue") error_msg = "Oh no! 💥 💔 💥" if required_version and required_version != __version__: err(f"{error_msg} The required version `{required_version}` does not match" f" the running version `{__version__}`!") ctx.exit(1) if ipynb and pyi: err("Cannot pass both `pyi` and `ipynb` flags!") ctx.exit(1) write_back = WriteBack.from_configuration(check=check, diff=diff, color=color) if target_version: versions = set(target_version) else: # We'll autodetect later. versions = set() mode = Mode( target_versions=versions, line_length=line_length, is_pyi=pyi, is_ipynb=ipynb, string_normalization=not skip_string_normalization, magic_trailing_comma=not skip_magic_trailing_comma, experimental_string_processing=experimental_string_processing, ) if code is not None: # Run in quiet mode by default with -c; the extra output isn't useful. # You can still pass -v to get verbose output. quiet = True report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) if code is not None: reformat_code(content=code, fast=fast, write_back=write_back, mode=mode, report=report) else: try: sources = get_sources( ctx=ctx, src=src, quiet=quiet, verbose=verbose, include=include, exclude=exclude, extend_exclude=extend_exclude, force_exclude=force_exclude, report=report, stdin_filename=stdin_filename, ) except GitWildMatchPatternError: ctx.exit(1) path_empty( sources, "No Python files are present to be formatted. Nothing to do 😴", quiet, verbose, ctx, ) if len(sources) == 1: reformat_one( src=sources.pop(), fast=fast, write_back=write_back, mode=mode, report=report, ) else: reformat_many( sources=sources, fast=fast, write_back=write_back, mode=mode, report=report, workers=workers, ) if verbose or not quiet: out(error_msg if report.return_code else "All done! ✨ 🍰 ✨") if code is None: click.echo(str(report), err=True) ctx.exit(report.return_code)
def main( # noqa: C901 ctx: click.Context, code: Optional[str], line_length: int, target_version: List[TargetVersion], check: bool, diff: bool, color: bool, fast: bool, pyi: bool, ipynb: bool, python_cell_magics: Sequence[str], skip_string_normalization: bool, skip_magic_trailing_comma: bool, experimental_string_processing: bool, preview: bool, quiet: bool, verbose: bool, required_version: Optional[str], include: Pattern[str], exclude: Optional[Pattern[str]], extend_exclude: Optional[Pattern[str]], force_exclude: Optional[Pattern[str]], stdin_filename: Optional[str], workers: int, src: Tuple[str, ...], config: Optional[str], ) -> None: """The uncompromising code formatter.""" ctx.ensure_object(dict) if src and code is not None: out( main.get_usage(ctx) + "\n\n'SRC' and 'code' cannot be passed simultaneously.") ctx.exit(1) if not src and code is None: out(main.get_usage(ctx) + "\n\nOne of 'SRC' or 'code' is required.") ctx.exit(1) root, method = find_project_root(src) if code is None else (None, None) ctx.obj["root"] = root if verbose: if root: out( f"Identified `{root}` as project root containing a {method}.", fg="blue", ) normalized = [(normalize_path_maybe_ignore(Path(source), root), source) for source in src] srcs_string = ", ".join([ f'"{_norm}"' if _norm else f'\033[31m"{source} (skipping - invalid)"\033[34m' for _norm, source in normalized ]) out(f"Sources to be formatted: {srcs_string}", fg="blue") if config: config_source = ctx.get_parameter_source("config") user_level_config = str(find_user_pyproject_toml()) if config == user_level_config: out( "Using configuration from user-level config at " f"'{user_level_config}'.", fg="blue", ) elif config_source in ( ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP, ): out("Using configuration from project root.", fg="blue") else: out(f"Using configuration in '{config}'.", fg="blue") error_msg = "Oh no! 💥 💔 💥" if (required_version and required_version != __version__ and required_version != __version__.split(".")[0]): err(f"{error_msg} The required version `{required_version}` does not match" f" the running version `{__version__}`!") ctx.exit(1) if ipynb and pyi: err("Cannot pass both `pyi` and `ipynb` flags!") ctx.exit(1) write_back = WriteBack.from_configuration(check=check, diff=diff, color=color) if target_version: versions = set(target_version) else: # We'll autodetect later. versions = set() mode = Mode( target_versions=versions, line_length=line_length, is_pyi=pyi, is_ipynb=ipynb, string_normalization=not skip_string_normalization, magic_trailing_comma=not skip_magic_trailing_comma, experimental_string_processing=experimental_string_processing, preview=preview, python_cell_magics=set(python_cell_magics), ) if code is not None: # Run in quiet mode by default with -c; the extra output isn't useful. # You can still pass -v to get verbose output. quiet = True report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) if code is not None: reformat_code(content=code, fast=fast, write_back=write_back, mode=mode, report=report) else: try: sources = get_sources( ctx=ctx, src=src, quiet=quiet, verbose=verbose, include=include, exclude=exclude, extend_exclude=extend_exclude, force_exclude=force_exclude, report=report, stdin_filename=stdin_filename, ) except GitWildMatchPatternError: ctx.exit(1) path_empty( sources, "No Python files are present to be formatted. Nothing to do 😴", quiet, verbose, ctx, ) if len(sources) == 1: reformat_one( src=sources.pop(), fast=fast, write_back=write_back, mode=mode, report=report, ) else: reformat_many( sources=sources, fast=fast, write_back=write_back, mode=mode, report=report, workers=workers, ) if verbose or not quiet: if code is None and (verbose or report.change_count or report.failure_count): out() out(error_msg if report.return_code else "All done! ✨ 🍰 ✨") if code is None: click.echo(str(report), err=True) ctx.exit(report.return_code)
def main( ctx: click.Context, code: Optional[str], line_length: int, target_version: List[TargetVersion], check: bool, diff: bool, color: bool, fast: bool, pyi: bool, skip_string_normalization: bool, skip_magic_trailing_comma: bool, experimental_string_processing: bool, quiet: bool, verbose: bool, include: Pattern, exclude: Optional[Pattern], extend_exclude: Optional[Pattern], force_exclude: Optional[Pattern], stdin_filename: Optional[str], src: Tuple[str, ...], config: Optional[str], ) -> None: """The uncompromising code formatter.""" write_back = WriteBack.from_configuration(check=check, diff=diff, color=color) if target_version: versions = set(target_version) else: # We'll autodetect later. versions = set() mode = Mode( target_versions=versions, line_length=line_length, is_pyi=pyi, string_normalization=not skip_string_normalization, magic_trailing_comma=not skip_magic_trailing_comma, experimental_string_processing=experimental_string_processing, ) if config and verbose: out(f"Using configuration from {config}.", bold=False, fg="blue") if code is not None: print(format_str(code, mode=mode)) ctx.exit(0) report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) sources = get_sources( ctx=ctx, src=src, quiet=quiet, verbose=verbose, include=include, exclude=exclude, extend_exclude=extend_exclude, force_exclude=force_exclude, report=report, stdin_filename=stdin_filename, ) path_empty( sources, "No Python files are present to be formatted. Nothing to do 😴", quiet, verbose, ctx, ) if len(sources) == 1: reformat_one( src=sources.pop(), fast=fast, write_back=write_back, mode=mode, report=report, ) else: reformat_many(sources=sources, fast=fast, write_back=write_back, mode=mode, report=report) if verbose or not quiet: out("Oh no! 💥 💔 💥" if report.return_code else "All done! ✨ 🍰 ✨") click.secho(str(report), err=True) ctx.exit(report.return_code)