Пример #1
0
def setup_formatter(snake: str, line_length: int = DEFAULT_LINE_LENGTH):
    stream = StringIO(snake)
    smk = Snakefile(stream)
    return Formatter(smk, line_length=line_length)
Пример #2
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 🎉")
Пример #3
0
def setup_formatter(snake: str, line_length: int = None, black_config_file=None):
    stream = StringIO(snake)
    smk = Snakefile(stream)
    return Formatter(smk, line_length=line_length, black_config_file=black_config_file)