def setup_formatter(snake: str, line_length: int = DEFAULT_LINE_LENGTH): stream = StringIO(snake) smk = Snakefile(stream) return Formatter(smk, line_length=line_length)
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 🎉")
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)