remove usage of Flask-Script

Use flask.cli instead with compatibility layer for existing commands,
such as "runserver".
This commit is contained in:
Glandos 2021-06-06 14:30:52 +02:00
parent eff871e83c
commit 74e222f1a1
5 changed files with 77 additions and 89 deletions

View file

@ -38,7 +38,7 @@ update: remove-install-stamp install ## Update the dependencies
.PHONY: serve .PHONY: serve
serve: install ## Run the ihatemoney server serve: install ## Run the ihatemoney server
@echo 'Running ihatemoney on http://localhost:5000' @echo 'Running ihatemoney on http://localhost:5000'
$(PYTHON) -m ihatemoney.manage runserver $(PYTHON) -m ihatemoney.manage run
.PHONY: test .PHONY: test
test: install-dev ## Run the tests test: install-dev ## Run the tests

View file

@ -61,7 +61,7 @@ Test it
Once installed, you can start a test server:: Once installed, you can start a test server::
ihatemoney runserver ihatemoney run
And point your browser at `http://localhost:5000 <http://localhost:5000>`_. And point your browser at `http://localhost:5000 <http://localhost:5000>`_.

View file

@ -5,8 +5,8 @@ import os
import random import random
import sys import sys
from flask_migrate import Migrate, MigrateCommand import click
from flask_script import Command, Manager, Option from flask.cli import FlaskGroup
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from ihatemoney.models import Project, db from ihatemoney.models import Project, db
@ -14,31 +14,48 @@ from ihatemoney.run import create_app
from ihatemoney.utils import create_jinja_env from ihatemoney.utils import create_jinja_env
class GeneratePasswordHash(Command): @click.group(cls=FlaskGroup, create_app=create_app)
def cli():
"""IHateMoney Management script"""
@cli.command(
context_settings={"ignore_unknown_options": True, "allow_extra_args": True}
)
@click.pass_context
def runserver(ctx):
"""Deprecated, use the "run" command instead"""
click.secho(
'"runserver" is deprecated, please use the standard "run" flask command',
fg="red",
)
run = cli.get_command(ctx, "run")
ctx.forward(run)
@click.command(name="generate_password_hash")
def password_hash():
"""Get password from user and hash it without printing it in clear text.""" """Get password from user and hash it without printing it in clear text."""
password = getpass.getpass(prompt="Password: ")
def run(self): print(generate_password_hash(password))
password = getpass.getpass(prompt="Password: ")
print(generate_password_hash(password))
class GenerateConfig(Command): @click.command()
def get_options(self): @click.argument(
return [ "config_file",
Option( type=click.Choice(
"config_file", [
choices=[ "ihatemoney.cfg",
"ihatemoney.cfg", "apache-vhost.conf",
"apache-vhost.conf", "gunicorn.conf.py",
"gunicorn.conf.py", "supervisord.conf",
"supervisord.conf", "nginx.conf",
"nginx.conf",
],
)
] ]
),
)
def generate_config(config_file):
"""Generate front-end server configuration"""
@staticmethod
def gen_secret_key(): def gen_secret_key():
return "".join( return "".join(
[ [
@ -49,59 +66,33 @@ class GenerateConfig(Command):
] ]
) )
def run(self, config_file): env = create_jinja_env("conf-templates", strict_rendering=True)
env = create_jinja_env("conf-templates", strict_rendering=True) template = env.get_template(f"{config_file}.j2")
template = env.get_template(f"{config_file}.j2")
bin_path = os.path.dirname(sys.executable) bin_path = os.path.dirname(sys.executable)
pkg_path = os.path.abspath(os.path.dirname(__file__)) pkg_path = os.path.abspath(os.path.dirname(__file__))
print( print(
template.render( template.render(
pkg_path=pkg_path, pkg_path=pkg_path,
bin_path=bin_path, bin_path=bin_path,
sys_prefix=sys.prefix, sys_prefix=sys.prefix,
secret_key=self.gen_secret_key(), secret_key=gen_secret_key(),
)
) )
)
class DeleteProject(Command): @cli.command()
def run(self, project_name): @click.argument("project_name")
demo_project = Project.query.get(project_name) def delete_project(project_name):
db.session.delete(demo_project) """Delete a project"""
project = Project.query.get(project_name)
if project is None:
click.secho(f'Project "{project_name}" not found', fg="red")
else:
db.session.delete(project)
db.session.commit() db.session.commit()
def main():
QUIET_COMMANDS = ("generate_password_hash", "generate-config")
exception = None
backup_stderr = sys.stderr
# Hack to divert stderr for commands generating content to stdout
# to avoid confusing the user
if len(sys.argv) > 1 and sys.argv[1] in QUIET_COMMANDS:
sys.stderr = open(os.devnull, "w")
try:
app = create_app()
Migrate(app, db)
except Exception as e:
exception = e
# Restore stderr
sys.stderr = backup_stderr
if exception:
raise exception
manager = Manager(app)
manager.add_command("db", MigrateCommand)
manager.add_command("generate_password_hash", GeneratePasswordHash)
manager.add_command("generate-config", GenerateConfig)
manager.add_command("delete-project", DeleteProject)
manager.run()
if __name__ == "__main__": if __name__ == "__main__":
main() cli()

View file

@ -1,4 +1,3 @@
import io
import os import os
import smtplib import smtplib
import socket import socket
@ -9,7 +8,7 @@ from sqlalchemy import orm
from ihatemoney import models from ihatemoney import models
from ihatemoney.currency_convertor import CurrencyConverter from ihatemoney.currency_convertor import CurrencyConverter
from ihatemoney.manage import DeleteProject, GenerateConfig, GeneratePasswordHash from ihatemoney.manage import delete_project, generate_config, password_hash
from ihatemoney.run import load_configuration from ihatemoney.run import load_configuration
from ihatemoney.tests.common.ihatemoney_testcase import BaseTestCase, IhatemoneyTestCase from ihatemoney.tests.common.ihatemoney_testcase import BaseTestCase, IhatemoneyTestCase
@ -82,28 +81,23 @@ class CommandTestCase(BaseTestCase):
- raise no exception - raise no exception
- produce something non-empty - produce something non-empty
""" """
cmd = GenerateConfig() runner = self.app.test_cli_runner()
for config_file in cmd.get_options()[0].kwargs["choices"]: for config_file in generate_config.params[0].type.choices:
with patch("sys.stdout", new=io.StringIO()) as stdout: result = runner.invoke(generate_config, config_file)
cmd.run(config_file) self.assertNotEqual(len(result.output.strip()), 0)
print(stdout.getvalue())
self.assertNotEqual(len(stdout.getvalue().strip()), 0)
def test_generate_password_hash(self): def test_generate_password_hash(self):
cmd = GeneratePasswordHash() runner = self.app.test_cli_runner()
with patch("sys.stdout", new=io.StringIO()) as stdout, patch( with patch("getpass.getpass", new=lambda prompt: "secret"):
"getpass.getpass", new=lambda prompt: "secret" result = runner.invoke(password_hash)
): # NOQA self.assertEqual(len(result.output.strip()), 94)
cmd.run()
print(stdout.getvalue())
self.assertEqual(len(stdout.getvalue().strip()), 189)
def test_demo_project_deletion(self): def test_demo_project_deletion(self):
self.create_project("demo") self.create_project("demo")
self.assertEquals(models.Project.query.get("demo").name, "demo") self.assertEquals(models.Project.query.get("demo").name, "demo")
cmd = DeleteProject() runner = self.app.test_cli_runner()
cmd.run("demo") runner.invoke(delete_project, "demo")
self.assertEqual(len(models.Project.query.all()), 0) self.assertEqual(len(models.Project.query.all()), 0)

View file

@ -32,7 +32,6 @@ install_requires =
Flask-Mail~=0.9 Flask-Mail~=0.9
Flask-Migrate>=2.5.3,<3 # Not following semantic versioning (e.g. https://github.com/miguelgrinberg/flask-migrate/commit/1af28ba273de6c88544623b8dc02dd539340294b) Flask-Migrate>=2.5.3,<3 # Not following semantic versioning (e.g. https://github.com/miguelgrinberg/flask-migrate/commit/1af28ba273de6c88544623b8dc02dd539340294b)
Flask-RESTful~=0.3 Flask-RESTful~=0.3
Flask-Script~=2.0
Flask-SQLAlchemy~=2.4 Flask-SQLAlchemy~=2.4
Flask-WTF~=0.14,>=0.14.3 # See b76da172652da94c1f9c4b2fdd965375da8a2c3f Flask-WTF~=0.14,>=0.14.3 # See b76da172652da94c1f9c4b2fdd965375da8a2c3f
WTForms~=2.2.1,<2.3 # See #567 WTForms~=2.2.1,<2.3 # See #567
@ -58,8 +57,12 @@ doc =
docutils==0.17.1 docutils==0.17.1
[options.entry_points] [options.entry_points]
flask.commands =
generate_password_hash = ihatemoney.manage:password_hash
generate-config = ihatemoney.manage:generate_config
console_scripts = console_scripts =
ihatemoney = ihatemoney.manage:main ihatemoney = ihatemoney.manage:cli
paste.app_factory = paste.app_factory =
main = ihatemoney.run:main main = ihatemoney.run:main