def main(args):
    """
    This matches the default behaviour of the black formatter.
    This script will raise an exception if changes by black are required.
    """
    sources = set()
    root = Path(os.getcwd())
    p = Path(".")
    include_regex = re_compile_maybe_verbose(DEFAULT_INCLUDES)
    exclude_regex = re_compile_maybe_verbose(DEFAULT_EXCLUDES)
    report = Report()
    sources.update(
        gen_python_files_in_dir(p, root, include_regex, exclude_regex, report,
                                get_gitignore(root)))

    # To conform to flake8 line length used for this project.
    mode = FileMode(line_length=79)
    write_back = WriteBack.from_configuration(check=False, diff=not args.force)

    reformat_many(
        sources=sources,
        fast=False,
        write_back=write_back,
        mode=mode,
        report=report,
    )

    if report.change_count != 0:
        exception_msg = """
                           Black formatter suggests formatting changes required
                           Run with '-f' option to automatically format.
                        """
        raise Exception(exception_msg)
Пример #2
0
    def test_gitignore_paths_excluded(self, ):
        include = re.compile(r"(\.smk$|^Snakefile)")
        exclude = re.compile(r"")

        ignored_gitignore = get_gitignore(Path())
        actual_gitignored = "Snakefile*"
        actual = self.find_files_to_format(include,
                                           exclude,
                                           ignored_gitignore,
                                           gitignored=actual_gitignored)
        expected = Counter(["rules/map.smk", "rules/test/test.smk"])
        assert actual == expected
Пример #3
0
def main(
    ctx: click.Context,
    line_length: int,
    include: str,
    exclude: str,
    src: List[PathLike],
    verbose: bool,
):
    """The uncompromising Snakemake code formatter."""
    log_level = logging.DEBUG if verbose else logging.INFO
    logging.basicConfig(format="[%(levelname)s] %(message)s", level=log_level)

    if not src:
        click.echo("No path provided. Nothing to do 😴", err=True)
        ctx.exit(0)

    if "-" in src and len(src) > 1:
        raise click.BadArgumentUsage("Cannot mix stdin (-) with other files")

    try:
        include_regex = construct_regex(include)
    except re.error:
        raise InvalidRegularExpression(
            f"Invalid regular expression for --include given: {include!r}")

    try:
        exclude_regex = construct_regex(exclude)
    except re.error:
        raise InvalidRegularExpression(
            f"Invalid regular expression for --exclude given: {exclude!r}")

    sources: Set[PathLike] = set()
    root = Path()
    gitignore = get_gitignore(Path())
    for s in src:
        path = Path(s)
        if path.is_dir():
            sources.update(
                get_snakefiles_in_dir(path, root, include_regex, exclude_regex,
                                      gitignore))
        elif path.is_file() or s == "-":
            # if a file was explicitly given, we don't care about its extension
            sources.add(path)
        else:
            logging.warning(f"ignoring invalid path: {s}")

    print(f"Formatting:")
    for s in sources:
        print(s)
Пример #4
0
 def find_files_to_format(self,
                          include,
                          exclude,
                          gitignore,
                          gitignored: Optional[str] = None):
     with tempfile.TemporaryDirectory() as tmpdir:
         abs_tmpdir = Path(tmpdir).resolve()
         self.create_temp_filesystem_in(abs_tmpdir)
         if gitignored is not None:
             with (abs_tmpdir / ".gitignore").open("w") as fout:
                 fout.write(gitignored)
             gitignore = get_gitignore(abs_tmpdir)
         snakefiles = get_snakefiles_in_dir(
             path=abs_tmpdir,
             include=include,
             exclude=exclude,
             gitignore=gitignore,
         )
         snakefiles = list(
             map(lambda p: str(p.relative_to(abs_tmpdir)), snakefiles))
     return Counter(snakefiles)
Пример #5
0
def get_source_files(paths: Iterable[str]) -> Iterable[Path]:
    report = Report()
    root = find_project_root((f for f in paths))
    sources: Set[Path] = set()
    for filename in paths:
        path = Path(filename)
        if path.is_dir():
            sources.update(
                gen_python_files_in_dir(
                    path=path,
                    root=root,
                    include=INCLUDES,
                    exclude=EXCLUDES,
                    report=report,
                    gitignore=get_gitignore(root),
                ))
        elif path.is_file():
            sources.add(path)
        else:
            print(f"Error: invalid path: {path}")
            exit(1)
    return sources
Пример #6
0
def collect_files(src, include, exclude):
    root = black.find_project_root(tuple(src))
    gitignore = black.get_gitignore(root)
    report = black.Report()

    force_exclude = ""

    for path in src:
        if path.is_dir():
            yield from gen_python_files(
                path.iterdir(),
                root,
                include,
                exclude,
                force_exclude,
                report,
                gitignore,
            )
        elif path.is_file() or str(path) == "-":
            yield path
        else:
            print(f"invalid path: {path}", file=sys.stderr)
Пример #7
0
def collect_files(src, include, exclude, force_exclude):
    root = find_project_root(tuple(src))
    gitignore = black.get_gitignore(root)
    report = black.Report()

    for path in src:
        if path.is_dir():
            yield from gen_python_files(
                path.iterdir(),
                root,
                include,
                exclude,
                force_exclude,
                report,
                gitignore,
            )
        elif str(path) == "-":
            yield path
        elif path.is_file():
            normalized_path = black.normalize_path_maybe_ignore(
                path, root, report)
            if normalized_path is None:
                continue

            normalized_path = "/" + normalized_path
            # Hard-exclude any files that matches the `--force-exclude` regex.
            if force_exclude:
                force_exclude_match = force_exclude.search(normalized_path)
            else:
                force_exclude_match = None
            if force_exclude_match and force_exclude_match.group(0):
                report.path_ignored(
                    path, "matches the --force-exclude regular expression")
                continue

            yield path
        else:
            print(f"invalid path: {path}", file=sys.stderr)
Пример #8
0
def main(src):
    # Path handling inspired on the implementation of Black: https://github.com/psf/black
    root = find_project_root(src)
    gitignore = get_gitignore(root)
    sources: Set[Path] = set()

    for s in src:
        p = Path(s)
        if p.is_dir():
            sources.update(get_files_in_dir(p, root, gitignore))
        elif p.is_file() or s == "-":
            # if a file was explicitly given, we don't care about its extension
            sources.add(p)
        else:
            raise RuntimeError(f"invalid path: {s}")

    ok = True
    for source in sources:
        file_ok = check_file(source)
        if not file_ok:
            ok = False

    if not ok:
        sys.exit(1)
Пример #9
0
def main(
    ctx: click.Context,
    line_length: int,
    check: bool,
    diff: bool,
    compact_diff: bool,
    include: str,
    exclude: str,
    src: List[PathLike],
    config: Optional[PathLike],
    verbose: bool,
):
    """The uncompromising Snakemake code formatter.

    SRC specifies directories and files to format. Directories will be searched for
    file names that conform to the include/exclude patterns provided.

    Files are modified in-place by default; use diff, check, or
     `snakefmt - < Snakefile` to avoid this.
    """
    log_level = logging.DEBUG if verbose else logging.INFO
    logging.basicConfig(format="[%(levelname)s] %(message)s", level=log_level)

    if not src:
        click.echo(
            "No path provided. Nothing to do 😴. Call with -h for help.", err=True
        )
        ctx.exit(0)

    if "-" in src and len(src) > 1:
        raise click.BadArgumentUsage("Cannot mix stdin (-) with other files")

    if diff and compact_diff:
        logging.warning(
            "Both --diff and --compact-diff given. Returning compact diff..."
        )

    try:
        include_regex = construct_regex(include)
    except re.error:
        raise InvalidRegularExpression(
            f"Invalid regular expression for --include given: {include!r}"
        )

    try:
        exclude_regex = construct_regex(exclude)
    except re.error:
        raise InvalidRegularExpression(
            f"Invalid regular expression for --exclude given: {exclude!r}"
        )

    files_to_format: Set[PathLike] = set()
    gitignore = get_gitignore(Path())
    for path in src:
        path = Path(path)
        if path.name == "-" or path.is_file():
            # if a file was explicitly given, we don't care about its extension
            files_to_format.add(path)
        elif path.is_dir():
            files_to_format.update(
                get_snakefiles_in_dir(path, include_regex, exclude_regex, gitignore)
            )
        else:
            logging.warning(f"ignoring invalid path: {path}")

    differ = Diff(compact=compact_diff)
    files_changed, files_unchanged = 0, 0
    files_with_errors = 0
    for path in files_to_format:
        path_is_stdin = path.name == "-"
        if path_is_stdin:
            logging.debug("Formatting from stdin")
            path = sys.stdin
        else:
            logging.debug(f"Formatting {path}")

        try:
            original_content = path.read_text()
        except AttributeError:
            original_content = path.read()

        try:
            snakefile = Snakefile(StringIO(original_content))
            formatter = Formatter(
                snakefile, line_length=line_length, black_config_file=config
            )
            formatted_content = formatter.get_formatted()
        except Exception as error:
            if check:
                logging.error(f"'{error.__class__.__name__}: {error}' in file {path}")
                files_with_errors += 1
                continue
            else:
                raise error

        if check:
            is_changed = differ.is_changed(original_content, formatted_content)
            if is_changed:
                logging.debug("Formatted content is different from original")
                files_changed += 1
            else:
                files_unchanged += 1

        if diff or compact_diff:
            filename = "stdin" if path_is_stdin else str(path)
            click.echo(f"{'=' * 5}> Diff for {filename} <{'=' * 5}\n")
            difference = differ.compare(original_content, formatted_content)
            click.echo(difference)
        elif not any([check, diff, compact_diff]):
            if path_is_stdin:
                sys.stdout.write(formatted_content)
            else:
                write_file_back = differ.is_changed(original_content, formatted_content)
                if write_file_back:
                    logging.info(f"Writing formatted content to {path}")
                    with path.open("w") as out_handle:
                        out_handle.write(formatted_content)

    if check:
        if files_unchanged == len(files_to_format):
            logging.info(
                f"All {len(files_to_format)} file(s) would be left unchanged 🎉"
            )
            ctx.exit(ExitCode.NO_CHANGE.value)
        elif files_with_errors > 0:
            exit_value = ExitCode.ERROR.value
        elif files_changed > 0:
            exit_value = ExitCode.WOULD_CHANGE.value

        if files_with_errors > 0:
            logging.info(f"{files_with_errors} file(s) raised parsing errors 🤕")
        if files_changed > 0:
            logging.info(f"{files_changed} file(s) would be changed 😬")
        if files_unchanged > 0:
            logging.info(f"{files_unchanged} file(s) would be left unchanged 🎉")
        ctx.exit(exit_value)

    logging.info("All done 🎉")
Пример #10
0
def main(
    ctx: click.Context,
    code: Optional[str],
    line_length: int,
    target_version: List[TargetVersion],
    check: bool,
    diff: bool,
    fast: bool,
    pyi: bool,
    py36: bool,
    skip_string_normalization: bool,
    single_quotes: bool,
    quiet: bool,
    verbose: bool,
    include: str,
    exclude: str,
    src: Tuple[str],
    config: Optional[str],
) -> None:
    """The uncompromising code formatter."""
    write_back = WriteBack.from_configuration(check=check, diff=diff)
    if target_version:
        if py36:
            err('Cannot use both --target-version and --py36')
            ctx.exit(2)
        else:
            versions = set(target_version)
    elif py36:
        err('--py36 is deprecated and will be removed in a future version. '
            'Use --target-version py36 instead.')
        versions = PY36_VERSIONS
    else:
        # We'll autodetect later.
        versions = set()
    mode = FileMode(
        target_versions=versions,
        line_length=line_length,
        is_pyi=pyi,
        string_normalization=not skip_string_normalization,
    )

    if single_quotes:
        black.normalize_string_quotes = patched_normalize_string_quotes

    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)
    try:
        include_regex = re_compile_maybe_verbose(include)
    except re.error:
        err(f'Invalid regular expression for include given: {include!r}')
        ctx.exit(2)
    try:
        exclude_regex = re_compile_maybe_verbose(exclude)
    except re.error:
        err(f'Invalid regular expression for exclude given: {exclude!r}')
        ctx.exit(2)
    report = Report(check=check, quiet=quiet, verbose=verbose)
    root = find_project_root(src)
    sources: Set[Path] = set()
    path_empty(src=src, quiet=quiet, verbose=verbose, ctx=ctx, msg=None)
    for s in src:
        p = Path(s)
        if p.is_dir():
            sources.update(
                gen_python_files_in_dir(
                    p,
                    root,
                    include_regex,
                    exclude_regex,
                    report,
                    get_gitignore(root),
                ))
        elif p.is_file() or s == '-':
            # if a file was explicitly given, we don't care about its extension
            sources.add(p)
        else:
            err(f'invalid path: {s}')
    if len(sources) == 0:
        if verbose or not quiet:
            out('No Python files are present to be formatted. Nothing to do 😴')
        ctx.exit(0)

    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)
Пример #11
0
def cli(
    ctx: click.Context,
    line_length: int,
    check: bool,
    include: str,
    exclude: str,
    quiet: bool,
    verbose: bool,
    clear_output: bool,
    src: Tuple[str],
    config: Optional[str],
) -> None:
    """
    The uncompromising code formatter, for Jupyter notebooks.
    """
    write_back = black.WriteBack.from_configuration(check=check, diff=False)
    mode = black.FileMode(
        target_versions=black.PY36_VERSIONS,
        line_length=line_length,
        is_pyi=False,
        string_normalization=True,
    )

    if config and verbose:
        black.out(f"Using configuration from {config}.", bold=False, fg="blue")

    try:
        include_regex = black.re_compile_maybe_verbose(include)
    except re.error:
        black.err(f"Invalid regular expression for include given: {include!r}")
        ctx.exit(2)
    try:
        exclude_regex = black.re_compile_maybe_verbose(exclude)
    except re.error:
        black.err(f"Invalid regular expression for exclude given: {exclude!r}")
        ctx.exit(2)

    report = black.Report(check=check, quiet=quiet, verbose=verbose)
    root = black.find_project_root(src)
    sources: Set[Path] = set()
    for s in src:
        p = Path(s)
        if p.is_dir():
            sources.update(
                black.gen_python_files_in_dir(
                    p,
                    root,
                    include_regex,
                    exclude_regex,
                    report,
                    black.get_gitignore(root),
                ))
        elif p.is_file() or s == "-":
            # if a file was explicitly given, we don't care about its extension
            sources.add(p)
        else:
            black.err(f"invalid path: {s}")
    if len(sources) == 0:
        if verbose or not quiet:
            black.out("No paths given. Nothing to do.")
        ctx.exit(0)

    for source in sources:
        reformat_one(
            src=source,
            write_back=write_back,
            mode=mode,
            clear_output=clear_output,
            report=report,
            quiet=quiet,
            verbose=verbose,
        )

    if verbose or not quiet:
        black.out("All done!")
        click.secho(str(report), err=True)
    ctx.exit(report.return_code)