Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions bin/_file_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@


class FileFormatter(ABC):
check: bool
write: bool
args: argparse.Namespace

scanned_file_found: int
unformatted_file_count: int

def __init__(self, check: bool, write: bool) -> None:
self.check = check
self.write = write
arg_parser: argparse.ArgumentParser

def __init__(self, args: argparse.Namespace) -> None:
self.args = args

self.scanned_file_found = 0
self.unformatted_file_count = 0
Expand All @@ -26,9 +26,8 @@ def description() -> str:
"""Return the description of the formatter."""
return "JSON file formatter"

@staticmethod
@abstractmethod
def format(input_str: str) -> str:
def format(self, input_str: str) -> str:
"""Format method to formatted file content."""

@staticmethod
Expand All @@ -41,19 +40,26 @@ def decode_exception() -> Type[Exception]:
def file_extension() -> str:
"""Return file extension of files to format."""

@classmethod
def config_additional_args(cls) -> None:
"""Optionally configure additional args to arg parser."""
pass

def process_file(self, file_path: str) -> None:
with open(file_path, "r", encoding="utf-8") as f:
file_str = f.read()
try:
formatted_file_str = self.format(file_str)
except self.decode_exception() as error:
raise ValueError(f"{file_path}: Cannot decode the file content") from error
except Exception as error:
raise ValueError(f"{file_path}: Fail to process") from error
if file_str != formatted_file_str:
if self.write:
if self.args.write:
with open(file_path, "w", encoding="utf-8") as f:
f.write(formatted_file_str)
print(f"reformatted {file_path}")
if self.check:
if self.args.check:
print(f"would reformat {file_path}")
self.unformatted_file_count += 1
self.scanned_file_found += 1
Expand All @@ -69,25 +75,25 @@ def process_directory(self, directory_path: str) -> None:

def output_summary(self) -> None:
print(f"{self.scanned_file_found} file(s) scanned.")
if self.write:
if self.args.write:
print(f"{self.unformatted_file_count} file(s) reformatted.")
if self.check:
if self.args.check:
print(f"{self.unformatted_file_count} file(s) need reformat.")
if self.unformatted_file_count:
sys.exit(-1)
print("\033[1mAll done! ✨ 🍰 ✨\033[0m") # using bold font

@classmethod
def main(cls) -> None:
parser = argparse.ArgumentParser(description=cls.description())
parser.add_argument(
cls.arg_parser = argparse.ArgumentParser(description=cls.description())
cls.arg_parser.add_argument(
"paths",
metavar="file|dir",
type=str,
nargs="+",
help="file to format or directory containing files to format",
)
group = parser.add_mutually_exclusive_group()
group = cls.arg_parser.add_mutually_exclusive_group()
group.add_argument(
"-c",
"--check",
Expand All @@ -102,8 +108,10 @@ def main(cls) -> None:
help="Edit files in-place. (Beware!)",
)

args = parser.parse_args()
formatter = cls(args.check, args.write)
cls.config_additional_args()

args = cls.arg_parser.parse_args()
formatter = cls(args)

for path in args.paths:
if not os.path.exists(path):
Expand Down
3 changes: 1 addition & 2 deletions bin/json-format.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ class JSONFormatter(FileFormatter):
def description() -> str:
return "JSON file formatter"

@staticmethod
def format(input_str: str) -> str:
def format(self, input_str: str) -> str:
"""Opinionated format JSON file."""
obj = json.loads(input_str)
return json.dumps(obj, indent=2, sort_keys=True) + "\n"
Expand Down
30 changes: 27 additions & 3 deletions bin/yaml-format.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
"""JSON file formatter (without prettier)."""
import os
import sys
from textwrap import dedent

my_path = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, my_path + "/..")

import re
from io import StringIO
from typing import Type
from typing import Any, Dict, Type

# We use ruamel.yaml for parsing yaml files because it can preserve comments
from ruamel.yaml import YAML
Expand All @@ -27,10 +28,11 @@ class YAMLFormatter(FileFormatter):
def description() -> str:
return "YAML file formatter"

@staticmethod
def format(input_str: str) -> str:
def format(self, input_str: str) -> str:
"""Opinionated format YAML file."""
obj = yaml.load(input_str)
if self.args.add_test_metadata:
self._add_test_metadata(obj)
out_stream = StringIO()
yaml.dump(
obj,
Expand All @@ -39,6 +41,16 @@ def format(input_str: str) -> str:
# ruamel.yaml tends to add 2 empty lines at the bottom of the dump
return re.sub(r"\n+$", "\n", out_stream.getvalue())

@staticmethod
def _add_test_metadata(obj: Dict[str, Any]) -> None:
metadata = obj.get("Metadata", {})
if not metadata:
metadata = obj["Metadata"] = {}
sam_transform_test_value = metadata.get("SamTransformTest")
if sam_transform_test_value is not None and sam_transform_test_value is not True:
raise ValueError(f"Unexpected Metadata.SamTransformTest value {sam_transform_test_value}")
metadata["SamTransformTest"] = True

@staticmethod
def decode_exception() -> Type[Exception]:
return YAMLError
Expand All @@ -47,6 +59,18 @@ def decode_exception() -> Type[Exception]:
def file_extension() -> str:
return ".yaml"

@classmethod
def config_additional_args(cls) -> None:
cls.arg_parser.add_argument(
"--add-test-metadata",
action="store_true",
help=dedent(
"""\
Add the testing metadata to yaml file if it doesn't exist:
"Metadata: SamTransformTest: true" """
),
)


if __name__ == "__main__":
YAMLFormatter.main()