Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove -i and -o command line options #71

Merged
merged 12 commits into from
Jan 31, 2023
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
repos:
- repo: https://github.com/pycqa/isort
rev: 5.10.1
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 22.3.0
rev: 22.12.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 4.0.1
rev: 6.0.0
hooks:
- id: flake8
6 changes: 3 additions & 3 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
- id: djhtml
name: DjHTML
entry: djhtml -i
entry: djhtml
types: [html]
language: python
language_version: python3
Expand All @@ -9,7 +9,7 @@

- id: djcss
name: DjCSS
entry: djcss -i
entry: djcss
types_or: [css, scss]
language: python
language_version: python3
Expand All @@ -18,7 +18,7 @@

- id: djjs
name: DjJS
entry: djjs -i
entry: djjs
types: [javascript]
language: python
language_version: python3
Expand Down
19 changes: 7 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,24 @@ Install DjHTML with the following command:
## Usage

After installation you can indent templates using the `djhtml`
command. The default is to read from standard in and to write the
indented output to standard out. To modify the source file in-place,
use the `-i` / `--in-place` option and specify a filename:
command:

$ djhtml -i template.html
$ djhtml template.html
reindented template.html
1 template has been reindented.

Normally, the exit status of 0 means everything went well, regardless
of whether any files were changed. If any errors were encountered, the
exit status indicated the number of problematic files. However, when
the option `-c` / `--check` is used, the exit status is the number of
files that would have changed, but no changes are actually made.
An exit status of 0 means that everything went well, regardless of
whether any files were changed. When the option `-c` / `--check` is
used, the exit status is 1 when one or more files would have changed,
but no changes are actually made.

All available options are:

- `-h` / `--help`: show overview of available options
- `-i` / `--in-place`: modify files in-place
- `-c` / `--check`: don't modify files; the exit status is the number
JaapJoris marked this conversation as resolved.
Show resolved Hide resolved
of files that would have changed
- `-q` / `--quiet`: don't print any output
- `-t` / `--tabwidth`: set tabwidth (default is 4)
- `-o` / `--output-file`: write output to specified file


## `fmt:off` and `fmt:on`
Expand Down Expand Up @@ -157,7 +152,7 @@ the default tabwidth, you change the `entry` point of these hooks:
hooks:
- id: djhtml
# Use a tabwidth of 2 for HTML files
entry: djhtml -i -t 2
entry: djhtml --tabwith 2
- id: djcss
- id: djjs
```
Expand Down
250 changes: 96 additions & 154 deletions djhtml/__main__.py
Original file line number Diff line number Diff line change
@@ -1,210 +1,152 @@
import argparse
import sys
from pathlib import Path
"""
Entrypoint for all 4 command-line tools. Typical usage:

from . import modes
$ djhtml file1.html file2.html

Passing "-" as the filename will read from standard input and write to
standard output. Example usage:

def verify_changed(source, result):
"""
Verify that the source is either exactly equal to the result or
that the result has only changed by added or removed whitespace.
$ djhtml - < input.html > output.html
"""

"""
output_lines = result.split("\n")
changed = False
for line_nr, line in enumerate(source.split("\n")):
if line != output_lines[line_nr]:
changed = True
if line.strip() != output_lines[line_nr].strip():
raise IndentationError("Non-whitespace changes detected. Core dumped.")
import sys
from pathlib import Path

return changed
from . import modes, options
JaapJoris marked this conversation as resolved.
Show resolved Hide resolved


def main():
"""
Entrypoint for all 4 command-line tools. Typical usage:

$ djhtml -i file1.html file2.html
changed_files = 0
unchanged_files = 0
problematic_files = 0

"""
target_extension = ".html"
suffixes = [".html"]
Mode = modes.DjHTML
if sys.argv[0].endswith("djtxt"):
JaapJoris marked this conversation as resolved.
Show resolved Hide resolved
Mode = modes.DjTXT
target_extension = ".txt"
suffixes = [".txt"]
if sys.argv[0].endswith("djcss"):
Mode = modes.DjCSS
target_extension = ".css"
suffixes = [".css", ".scss"]
JaapJoris marked this conversation as resolved.
Show resolved Hide resolved
if sys.argv[0].endswith("djjs"):
Mode = modes.DjJS
target_extension = ".js"
suffixes = [".js"]

changed_files = 0
unchanged_files = 0
problematic_files = 0
if len(options.input_filenames) > 1 and "-" in options.input_filenames:
sys.exit("I’m sorry Dave, I’m afraid I can’t do that.")
JaapJoris marked this conversation as resolved.
Show resolved Hide resolved

parser = argparse.ArgumentParser(
description=(
"DjHTML is a fully automatic template indenter that works with mixed"
" HTML/CSS/Javascript templates that contain Django or Jinja template"
" tags. It works similar to other code-formatting tools such as Black and"
" interoperates nicely with pre-commit. Full documentation can be found at"
" https://github.com/rtts/djhtml"
),
)
parser.add_argument(
"-i", "--in-place", action="store_true", help="modify files in-place"
)
parser.add_argument("-c", "--check", action="store_true", help="don't modify files")
parser.add_argument("-q", "--quiet", action="store_true", help="be quiet")
parser.add_argument(
"-t",
"--tabwidth",
metavar="N",
type=int,
default=4,
help="tabwidth (default is 4)",
)
parser.add_argument(
"-o",
"--output-file",
metavar="filename",
default="-",
help="output filename",
)
parser.add_argument(
"input_filenames",
metavar="filenames",
nargs="*",
default=["-"],
help="input filenames (either paths or directories)",
)
parser.add_argument("-d", "--debug", action="store_true", help=argparse.SUPPRESS)
args = parser.parse_args()

if args.in_place and "-" in args.input_filenames:
sys.exit("I’m sorry Dave, I’m afraid I can’t do that")

if len(args.input_filenames) > 1 and not args.in_place and not args.check:
sys.exit("Will not modify files in-place without -i option")

for input_filename in _generate_files(args.input_filenames, target_extension):
for filename in _generate_filenames(options.input_filenames, suffixes):
# Read input file
try:
input_file = (
sys.stdin if input_filename == "-" else open(input_filename, "r")
)
input_file = sys.stdin if filename == "-" else open(filename, "r")
source = input_file.read()
except Exception as e:
problematic_files += 1
if not args.quiet:
print(f"Error opening {input_filename}: {e}", file=sys.stderr)
continue
_error(f"Cannot open {filename}: {e}")
finally:
input_file.close()

# Indent input file
try:
if args.debug:
if options.debug:
print(Mode(source).debug())
sys.exit()
result = Mode(source).indent(args.tabwidth)
result = Mode(source).indent(options.tabwidth)
except SyntaxError as e:
problematic_files += 1
if not args.quiet:
print(
f"Syntax error in {input_file.name}:"
f" {str(e) or e.__class__.__name__}",
file=sys.stderr,
)
_error(f"Syntax error in {filename}: {str(e) or e.__class__.__name__}")
continue
except Exception:
print(
f"\nFatal error while processing {input_file.name}\n\n"
_error(
f"Fatal error while processing {filename}\n\n"
" If you have time and are using the latest version, we\n"
" would very much appreciate if you opened an issue on\n"
" https://github.com/rtts/djhtml/issues\n",
file=sys.stderr,
" https://github.com/rtts/djhtml/issues\n"
)
raise
finally:
input_file.close()

changed = verify_changed(source, result)

# Print to stdout and exit
if not args.in_place and not args.check and args.output_file == "-":
if not args.quiet:
print(result, end="")
sys.exit(1 if args.check and changed else 0)
changed = _verify_changed(source, result)
if changed:
changed_files += 1
else:
unchanged_files += 1

# Write output file
if changed and args.check:
changed_files += 1
if filename == "-":
if not options.check:
print(result, end="")
elif changed:
output_filename = input_file.name if args.in_place else args.output_file
try:
output_file = open(output_filename, "w")
output_file = open(filename, "w")
JaapJoris marked this conversation as resolved.
Show resolved Hide resolved
output_file.write(result)
output_file.close()
changed_files += 1
except Exception as e:
changed_files -= 1
problematic_files += 1
if not args.quiet:
print(f"Error writing {output_filename}: {e}", file=sys.stderr)
_error(f"Error writing {filename}: {e}")
continue
if not args.quiet:
print(
f"reindented {output_file.name}",
file=sys.stderr,
)
else:
unchanged_files += 1
_info(f"reindented {output_file.name}")

# Print final summary
if not args.quiet:
s = "s" if changed_files != 1 else ""
have = "would have" if args.check else "have" if s else "has"
print(
f"{changed_files} template{s} {have} been reindented.",
file=sys.stderr,
s = "s" if changed_files != 1 else ""
have = "would have" if options.check else "have" if s else "has"
_info(f"{changed_files} template{s} {have} been reindented.")
if unchanged_files:
s = "s" if unchanged_files != 1 else ""
were = "were" if s else "was"
_info(f"{unchanged_files} template{s} {were} already perfect!")
if problematic_files:
s = "s" if problematic_files != 1 else ""
_info(
f"{problematic_files} template{s} could not be processed due to an error."
)
if unchanged_files:
s = "s" if unchanged_files != 1 else ""
were = "were" if s else "was"
print(
f"{unchanged_files} template{s} {were} already perfect!",
file=sys.stderr,
)
if problematic_files:
s = "s" if problematic_files != 1 else ""
print(
f"{problematic_files} template{s} could not be processed due to an"
" error.",
file=sys.stderr,
)

sys.exit(changed_files if args.check else problematic_files)
# Exit with appropriate exit status
if problematic_files:
JaapJoris marked this conversation as resolved.
Show resolved Hide resolved
sys.exit(1)
if options.check and changed_files:
sys.exit(1)
sys.exit(0)


def _generate_files(input_filenames, suffix):
for file_name in input_filenames:
if file_name == "-":
yield file_name
def _generate_filenames(paths, suffixes):
for filename in paths:
if filename == "-":
yield filename
else:
file_path = Path(file_name)
if file_path.is_file():
yield file_path
elif file_path.is_dir():
yield from _generate_files_from_folder(file_path, suffix)


def _generate_files_from_folder(folder, suffix):
for file_path in folder.iterdir():
if file_path.is_file() and file_path.suffix == suffix:
yield file_path
elif file_path.is_dir():
yield from _generate_files_from_folder(file_path, suffix)
path = Path(filename)
if path.is_file():
yield path
elif path.is_dir():
yield from _generate_filenames_from_directory(path, suffixes)


def _generate_filenames_from_directory(directory, suffixes):
for path in directory.iterdir():
if path.is_file() and path.suffix in suffixes:
yield path
elif path.is_dir():
yield from _generate_filenames_from_directory(path, suffixes)


def _verify_changed(source, result):
output_lines = result.split("\n")
changed = False
for line_nr, line in enumerate(source.split("\n")):
if line != output_lines[line_nr]:
changed = True
if line.strip() != output_lines[line_nr].strip():
raise IndentationError("Non-whitespace changes detected. Core dumped.")
return changed


def _info(msg):
JaapJoris marked this conversation as resolved.
Show resolved Hide resolved
if not options.quiet:
print(msg, file=sys.stderr)


def _error(msg):
_info(f"Error: {msg}")


if __name__ == "__main__":
Expand Down
Loading