diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py index 332f20c..cdee361 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py @@ -70,3 +70,5 @@ def register_commands(app): """Register Click commands.""" app.cli.add_command(cli.test) app.cli.add_command(cli.lint) + app.cli.add_command(cli.clean) + app.cli.add_command(cli.urls) diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/cli.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/cli.py index 5578d9c..2f67f95 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/cli.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/cli.py @@ -4,8 +4,10 @@ import os from glob import glob from subprocess import call -import click +import +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) @@ -13,7 +15,6 @@ TEST_PATH = os.path.join(PROJECT_ROOT, 'tests') @click.command() -@with_appcontext def test(): """Run the tests.""" import pytest @@ -23,7 +24,6 @@ def test(): @click.command() @click.option('-f', '--fix-imports', default=False, is_flag=True, help='Fix imports using isort, before linting') -@with_appcontext def lint(fix_imports): """Lint and check code style with flake8 and isort.""" skip = ['requirements'] @@ -44,3 +44,82 @@ def lint(fix_imports): if fix_imports: execute_tool('Fixing import order', 'isort', '-rc') 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]))