2021-07-12 18:26:46 +00:00
|
|
|
#!/usr/bin/python
|
2021-07-13 17:25:29 +00:00
|
|
|
"""
|
|
|
|
|
Check Django template syntax.
|
2021-07-12 18:26:46 +00:00
|
|
|
|
|
|
|
|
usage::
|
|
|
|
|
|
|
|
|
|
djlint INPUT -e <extension>
|
|
|
|
|
|
2021-07-23 20:58:24 +00:00
|
|
|
options:
|
|
|
|
|
|
|
|
|
|
--check | will check html formatting for needed changes
|
|
|
|
|
--reformat | will reformat html
|
|
|
|
|
|
2021-07-12 18:26:46 +00:00
|
|
|
"""
|
2021-07-13 17:25:29 +00:00
|
|
|
|
2021-07-12 18:26:46 +00:00
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
from concurrent.futures import ProcessPoolExecutor
|
2021-07-23 20:58:24 +00:00
|
|
|
from functools import partial
|
2021-07-12 18:26:46 +00:00
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import click
|
|
|
|
|
from click import echo
|
|
|
|
|
from colorama import Fore, Style, deinit, init
|
|
|
|
|
|
2021-07-23 20:58:24 +00:00
|
|
|
from djlint.lint import lint_file
|
|
|
|
|
from djlint.reformat import reformat_file
|
2021-07-12 18:26:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_src(src: Path, extension=None):
|
|
|
|
|
"""Get source files."""
|
|
|
|
|
if Path.is_file(src):
|
|
|
|
|
return [src]
|
|
|
|
|
|
|
|
|
|
# remove leading . from extension
|
|
|
|
|
extension = extension[1:] if extension.startswith(".") else extension
|
|
|
|
|
|
|
|
|
|
paths = list(src.glob(r"**/*.%s" % extension))
|
|
|
|
|
|
|
|
|
|
if len(paths) == 0:
|
2021-07-23 20:58:24 +00:00
|
|
|
echo(Fore.BLUE + "No files to check! 😢")
|
2021-07-12 18:26:46 +00:00
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
return paths
|
|
|
|
|
|
|
|
|
|
|
2021-07-13 17:25:29 +00:00
|
|
|
def build_output(error):
|
|
|
|
|
"""Build output for file errors."""
|
|
|
|
|
errors = sorted(list(error.values())[0], key=lambda x: int(x["line"].split(":")[0]))
|
|
|
|
|
|
|
|
|
|
if len(errors) == 0:
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
echo(
|
|
|
|
|
"{}\n{}\n{}===============================".format(
|
|
|
|
|
Fore.GREEN + Style.BRIGHT, list(error.keys())[0], Style.DIM
|
|
|
|
|
)
|
|
|
|
|
+ Style.RESET_ALL
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for message in errors:
|
|
|
|
|
error = bool(message["code"][:1] == "E")
|
|
|
|
|
echo(
|
|
|
|
|
"{} {} {} {} {}".format(
|
|
|
|
|
(Fore.RED if error else Fore.YELLOW),
|
|
|
|
|
message["code"] + Style.RESET_ALL,
|
|
|
|
|
Fore.BLUE + message["line"] + Style.RESET_ALL,
|
|
|
|
|
message["message"],
|
|
|
|
|
Fore.BLUE + message["match"],
|
|
|
|
|
),
|
|
|
|
|
err=False,
|
|
|
|
|
)
|
|
|
|
|
return len(errors)
|
|
|
|
|
|
|
|
|
|
|
2021-07-23 20:58:24 +00:00
|
|
|
def build_check_output(errors, quiet):
|
|
|
|
|
"""Build output for reformat check."""
|
|
|
|
|
if len(errors) == 0:
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
color = {"-": Fore.YELLOW, "+": Fore.GREEN, "@": Style.BRIGHT + Fore.BLUE}
|
|
|
|
|
|
|
|
|
|
if quiet is True or len(list(errors.values())[0]) == 0:
|
|
|
|
|
echo(
|
|
|
|
|
Fore.GREEN
|
|
|
|
|
+ Style.BRIGHT
|
|
|
|
|
+ str(list(errors.keys())[0])
|
|
|
|
|
+ Style.DIM
|
|
|
|
|
+ Style.RESET_ALL
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
echo(
|
|
|
|
|
"{}\n{}\n{}===============================".format(
|
|
|
|
|
Fore.GREEN + Style.BRIGHT, list(errors.keys())[0], Style.DIM
|
|
|
|
|
)
|
|
|
|
|
+ Style.RESET_ALL
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for diff in list(errors.values())[0]:
|
|
|
|
|
echo(
|
|
|
|
|
"{}{}{}".format(
|
|
|
|
|
color.get(diff[:1], Style.RESET_ALL), diff, Style.RESET_ALL
|
|
|
|
|
),
|
|
|
|
|
err=False,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return len(list(filter(lambda x: len(x) > 0, errors.values())))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_quantity(size: int):
|
|
|
|
|
"""Count files in a list."""
|
|
|
|
|
return "%d file%s" % (size, ("s" if size > 1 else ""))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_quantity_tense(size: int):
|
|
|
|
|
"""Count files in a list."""
|
|
|
|
|
return "%d file%s %s" % (
|
|
|
|
|
size,
|
|
|
|
|
("s" if size > 1 or size == 0 else ""),
|
|
|
|
|
("were" if size > 1 or size == 0 else "was"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2021-07-12 18:26:46 +00:00
|
|
|
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
|
|
|
|
|
@click.argument(
|
|
|
|
|
"src",
|
|
|
|
|
type=click.Path(
|
|
|
|
|
exists=True, file_okay=True, dir_okay=True, readable=True, allow_dash=True
|
|
|
|
|
),
|
|
|
|
|
nargs=1,
|
|
|
|
|
metavar="SRC ...",
|
|
|
|
|
)
|
|
|
|
|
@click.option(
|
|
|
|
|
"-e",
|
|
|
|
|
"--extension",
|
|
|
|
|
type=str,
|
|
|
|
|
default="html",
|
|
|
|
|
help="File extension to lint",
|
|
|
|
|
show_default=True,
|
|
|
|
|
)
|
2021-07-23 20:58:24 +00:00
|
|
|
@click.option(
|
|
|
|
|
"--reformat",
|
|
|
|
|
is_flag=True,
|
|
|
|
|
help="Reformat the file.",
|
|
|
|
|
)
|
|
|
|
|
@click.option(
|
|
|
|
|
"--check",
|
|
|
|
|
is_flag=True,
|
|
|
|
|
help="Reformat the file.",
|
|
|
|
|
)
|
|
|
|
|
@click.option(
|
|
|
|
|
"--quiet",
|
|
|
|
|
is_flag=True,
|
|
|
|
|
help="Reformat the file.",
|
|
|
|
|
)
|
|
|
|
|
def main(src: str, extension: str, reformat: bool, check: bool, quiet: bool):
|
2021-07-12 18:26:46 +00:00
|
|
|
"""Djlint django template files."""
|
|
|
|
|
file_list = get_src(Path(src), extension)
|
|
|
|
|
|
|
|
|
|
if len(file_list) == 0:
|
|
|
|
|
return
|
|
|
|
|
|
2021-07-23 20:58:24 +00:00
|
|
|
file_quantity = build_quantity(len(file_list))
|
2021-07-12 19:40:08 +00:00
|
|
|
|
2021-07-23 20:58:24 +00:00
|
|
|
message = "Reformatt" if reformat is True else "Lint"
|
|
|
|
|
|
|
|
|
|
echo(
|
|
|
|
|
"%sing %s!\n"
|
|
|
|
|
% (
|
|
|
|
|
message,
|
|
|
|
|
file_quantity,
|
|
|
|
|
)
|
|
|
|
|
)
|
2021-07-12 19:40:08 +00:00
|
|
|
|
2021-07-12 18:26:46 +00:00
|
|
|
worker_count = os.cpu_count()
|
|
|
|
|
|
|
|
|
|
if sys.platform == "win32":
|
|
|
|
|
# Work around https://bugs.python.org/issue26903
|
|
|
|
|
worker_count = min(worker_count, 60)
|
|
|
|
|
|
|
|
|
|
with ProcessPoolExecutor(max_workers=worker_count) as exe:
|
2021-07-23 20:58:24 +00:00
|
|
|
if reformat is True:
|
|
|
|
|
func = partial(reformat_file, check)
|
|
|
|
|
file_errors = exe.map(func, file_list)
|
|
|
|
|
else:
|
|
|
|
|
file_errors = exe.map(lint_file, file_list)
|
2021-07-12 18:26:46 +00:00
|
|
|
|
|
|
|
|
# format errors
|
2021-07-23 20:58:24 +00:00
|
|
|
success_message = ""
|
2021-07-12 19:40:08 +00:00
|
|
|
error_count = 0
|
2021-07-12 18:26:46 +00:00
|
|
|
|
2021-07-23 20:58:24 +00:00
|
|
|
if reformat is not True:
|
|
|
|
|
for error in file_errors:
|
|
|
|
|
error_count += build_output(error)
|
|
|
|
|
|
|
|
|
|
success_message = "%sed %s, found %d errors." % (
|
|
|
|
|
message,
|
|
|
|
|
file_quantity,
|
|
|
|
|
error_count,
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
for error in file_errors:
|
|
|
|
|
error_count += build_check_output(error, quiet)
|
|
|
|
|
tense_message = (
|
|
|
|
|
build_quantity(error_count) + " would be"
|
|
|
|
|
if check is True
|
|
|
|
|
else build_quantity_tense(error_count)
|
|
|
|
|
)
|
|
|
|
|
success_message = "%s updated." % tense_message
|
2021-07-12 18:26:46 +00:00
|
|
|
|
2021-07-12 19:40:08 +00:00
|
|
|
echo("\n%s\n" % (success_message))
|
2021-07-12 18:26:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
init(autoreset=True)
|
|
|
|
|
main()
|
|
|
|
|
deinit()
|