| | """ |
| | Summarize Black runs to users. |
| | """ |
| | from dataclasses import dataclass |
| | from enum import Enum |
| | from pathlib import Path |
| |
|
| | from click import style |
| |
|
| | from black.output import err, out |
| |
|
| |
|
| | class Changed(Enum): |
| | NO = 0 |
| | CACHED = 1 |
| | YES = 2 |
| |
|
| |
|
| | class NothingChanged(UserWarning): |
| | """Raised when reformatted code is the same as source.""" |
| |
|
| |
|
| | @dataclass |
| | class Report: |
| | """Provides a reformatting counter. Can be rendered with `str(report)`.""" |
| |
|
| | check: bool = False |
| | diff: bool = False |
| | quiet: bool = False |
| | verbose: bool = False |
| | change_count: int = 0 |
| | same_count: int = 0 |
| | failure_count: int = 0 |
| |
|
| | def done(self, src: Path, changed: Changed) -> None: |
| | """Increment the counter for successful reformatting. Write out a message.""" |
| | if changed is Changed.YES: |
| | reformatted = "would reformat" if self.check or self.diff else "reformatted" |
| | if self.verbose or not self.quiet: |
| | out(f"{reformatted} {src}") |
| | self.change_count += 1 |
| | else: |
| | if self.verbose: |
| | if changed is Changed.NO: |
| | msg = f"{src} already well formatted, good job." |
| | else: |
| | msg = f"{src} wasn't modified on disk since last run." |
| | out(msg, bold=False) |
| | self.same_count += 1 |
| |
|
| | def failed(self, src: Path, message: str) -> None: |
| | """Increment the counter for failed reformatting. Write out a message.""" |
| | err(f"error: cannot format {src}: {message}") |
| | self.failure_count += 1 |
| |
|
| | def path_ignored(self, path: Path, message: str) -> None: |
| | if self.verbose: |
| | out(f"{path} ignored: {message}", bold=False) |
| |
|
| | @property |
| | def return_code(self) -> int: |
| | """Return the exit code that the app should use. |
| | |
| | This considers the current state of changed files and failures: |
| | - if there were any failures, return 123; |
| | - if any files were changed and --check is being used, return 1; |
| | - otherwise return 0. |
| | """ |
| | |
| | |
| | if self.failure_count: |
| | return 123 |
| |
|
| | elif self.change_count and self.check: |
| | return 1 |
| |
|
| | return 0 |
| |
|
| | def __str__(self) -> str: |
| | """Render a color report of the current state. |
| | |
| | Use `click.unstyle` to remove colors. |
| | """ |
| | if self.check or self.diff: |
| | reformatted = "would be reformatted" |
| | unchanged = "would be left unchanged" |
| | failed = "would fail to reformat" |
| | else: |
| | reformatted = "reformatted" |
| | unchanged = "left unchanged" |
| | failed = "failed to reformat" |
| | report = [] |
| | if self.change_count: |
| | s = "s" if self.change_count > 1 else "" |
| | report.append( |
| | style(f"{self.change_count} file{s} ", bold=True, fg="blue") |
| | + style(f"{reformatted}", bold=True) |
| | ) |
| |
|
| | if self.same_count: |
| | s = "s" if self.same_count > 1 else "" |
| | report.append(style(f"{self.same_count} file{s} ", fg="blue") + unchanged) |
| | if self.failure_count: |
| | s = "s" if self.failure_count > 1 else "" |
| | report.append(style(f"{self.failure_count} file{s} {failed}", fg="red")) |
| | return ", ".join(report) + "." |
| |
|