| """ |
| 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) + "." |
|
|