# -*- coding: utf-8 -*- """Click commands.""" import os from glob import glob from subprocess import call import click from flask import current_app from flask.cli import with_appcontext from werkzeug.exceptions import MethodNotAllowed, NotFound HERE = os.path.abspath(os.path.dirname(__file__)) PROJECT_ROOT = os.path.join(HERE, os.pardir) TEST_PATH = os.path.join(PROJECT_ROOT, "tests") @click.command() def test(): """Run the tests.""" import pytest rv = pytest.main([TEST_PATH, "--verbose"]) exit(rv) @click.command() @click.option( "-f", "--fix-imports", default=True, is_flag=True, help="Fix imports using isort, before linting", ) @click.option( "-c", "--check", default=False, is_flag=True, help="Don't make any changes to files, just confirm they are formatted correctly", ) def lint(fix_imports, check): """Lint and check code style with black, flake8 and isort.""" skip = ["node_modules", "requirements", "migrations"] root_files = glob("*.py") root_directories = [ name for name in next(os.walk("."))[1] if not name.startswith(".") ] files_and_directories = [ arg for arg in root_files + root_directories if arg not in skip ] def execute_tool(description, *args): """Execute a checking tool with its arguments.""" command_line = list(args) + files_and_directories click.echo("{}: {}".format(description, " ".join(command_line))) rv = call(command_line) if rv != 0: exit(rv) isort_args = ["-rc"] black_args = [] if check: isort_args.append("-c") black_args.append("--check") if fix_imports: execute_tool("Fixing import order", "isort", *isort_args) execute_tool("Formatting style", "black", *black_args) execute_tool("Checking code style", "flake8") @click.command() def clean(): """Remove *.pyc and *.pyo files recursively starting at current directory. Borrowed from Flask-Script, converted to use Click. """ for dirpath, dirnames, filenames in os.walk("."): for filename in filenames: if filename.endswith(".pyc") or filename.endswith(".pyo"): full_pathname = os.path.join(dirpath, filename) click.echo("Removing {}".format(full_pathname)) os.remove(full_pathname) @click.command() @click.option("--url", default=None, help="Url to test (ex. /static/image.png)") @click.option( "--order", default="rule", help="Property on Rule to order by (default: rule)" ) @with_appcontext def urls(url, order): """Display all of the url matching routes for the project. Borrowed from Flask-Script, converted to use Click. """ rows = [] column_length = 0 column_headers = ("Rule", "Endpoint", "Arguments") if url: try: rule, arguments = current_app.url_map.bind("localhost").match( url, return_rule=True ) rows.append((rule.rule, rule.endpoint, arguments)) column_length = 3 except (NotFound, MethodNotAllowed) as e: rows.append(("<{}>".format(e), None, None)) column_length = 1 else: rules = sorted( current_app.url_map.iter_rules(), key=lambda rule: getattr(rule, order) ) for rule in rules: rows.append((rule.rule, rule.endpoint, None)) column_length = 2 str_template = "" table_width = 0 if column_length >= 1: max_rule_length = max(len(r[0]) for r in rows) max_rule_length = max_rule_length if max_rule_length > 4 else 4 str_template += "{:" + str(max_rule_length) + "}" table_width += max_rule_length if column_length >= 2: max_endpoint_length = max(len(str(r[1])) for r in rows) # max_endpoint_length = max(rows, key=len) max_endpoint_length = max_endpoint_length if max_endpoint_length > 8 else 8 str_template += " {:" + str(max_endpoint_length) + "}" table_width += 2 + max_endpoint_length if column_length >= 3: max_arguments_length = max(len(str(r[2])) for r in rows) max_arguments_length = max_arguments_length if max_arguments_length > 9 else 9 str_template += " {:" + str(max_arguments_length) + "}" table_width += 2 + max_arguments_length click.echo(str_template.format(*column_headers[:column_length])) click.echo("-" * table_width) for row in rows: click.echo(str_template.format(*row[:column_length]))