Skip to content

Commit

Permalink
Some improvements for Flask 1.0: flask.cli instead of Flask-Script, b…
Browse files Browse the repository at this point in the history
…etter config search, dotenv etc.
  • Loading branch information
andreymal committed May 21, 2018
1 parent 45835f2 commit ac2c9ce
Show file tree
Hide file tree
Showing 26 changed files with 179 additions and 95 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -29,6 +29,8 @@ dist/
*.mo

# mini_fiction stuff
/.env
/.flaskenv
/local_settings.py
/local/
/media/
Expand Down
8 changes: 4 additions & 4 deletions INSTALL.md
Expand Up @@ -10,7 +10,7 @@
* `mkdir media`
* `mini_fiction seed`
* `mini_fiction createsuperuser`
* `mini_fiction runserver`
* `mini_fiction run`


## Быстрый старт (развёртывание минимального окружения для разработки)
Expand Down Expand Up @@ -68,7 +68,7 @@ mini_fiction createsuperuser
* Запускаем сервер для разработки:

```
mini_fiction runserver
mini_fiction run
```

* Сайт создаст базу данных SQLite3 в текущем каталоге и станет доступен
Expand All @@ -94,8 +94,8 @@ class Local(Development):
... # здесь и далее все ваши настройки
```

* Запускаем `mini_fiction runserver` (или gunicorn или любой другой
wsgi-сервер) в том же каталоге, в котором находится `local_settings.py`:
* Запускаем `mini_fiction run` (или gunicorn или любой другой wsgi-сервер)
в том же каталоге, в котором находится `local_settings.py`:
сайт его найдёт и автоматически подхватит. Если же не подхватит, попробуйте
переменную окружения `PYTHONPATH=.` (не забудьте про `export PYTHONPATH` в
юниксовых шеллах).
Expand Down
22 changes: 20 additions & 2 deletions README.rst
Expand Up @@ -29,17 +29,35 @@ Quick start
mkdir media
mini_fiction seed
mini_fiction createsuperuser
mini_fiction runserver
mini_fiction run
Website will be available at ``http://localhost:5000/``, administration page is
``http://localhost:5000/admin/``.

Flask uses production environment by default. If you want to use
a development server, create ``.env`` file in your working directory and put
some settings here:

.. code::
FLASK_ENV=development
MINIFICTION_SETTINGS=mini_fiction.settings.Development
You can override this file using native environment variables:

.. code::
FLASK_ENV=production mini_fiction run
Configuration file
------------------

Just copy ``local_settings.example.py`` to ``local_settings.py`` and edit it.
Then run ``mini_fiction runserver`` in the same directory with this file.
Then run ``mini_fiction run`` in the same directory with this file.
Ensure that ``MINIFICTION_SETTINGS`` is not used in ``.env`` file.
Alternatively you can put ``MINIFICTION_SETTINGS=local_settings.Local`` to
``.env`` file if you think that explicit is better than implicit.

If mini_fiction can't import module ``local_settings``, try to set environment
variable ``PYTHONPATH=.`` (don't forget ``export PYTHONPATH`` for unix
Expand Down
2 changes: 2 additions & 0 deletions dotenv.example.txt
@@ -0,0 +1,2 @@
MINIFICTION_SETTINGS=mini_fiction.settings.Config
FLASK_ENV=production
12 changes: 10 additions & 2 deletions local_settings.example.py
Expand Up @@ -3,7 +3,7 @@

import logging

from mini_fiction.settings import Development
from mini_fiction.settings import Development, Test as BaseTest


class Local(Development):
Expand All @@ -20,7 +20,7 @@ class Local(Development):
# SERVER_NAME = 'example.org'
# PREFERRED_URL_SCHEME = 'http'

# LOGGER_LEVEL = logging.DEBUG
# LOGLEVEL = logging.DEBUG

# ADMINS = ['admin@example.org']
# ERROR_EMAIL_HANDLER_PARAMS = {'mailhost': ('127.0.0.1', 1025)}
Expand Down Expand Up @@ -57,3 +57,11 @@ class Local(Development):
# SPHINX_ROOT = '/path/to/directory/sphinx'
# SPHINX_SEARCHD = dict(Development.SPHINX_SEARCHD)
# SPHINX_SEARCHD['listen'] = '0.0.0.0:9306:mysql41'

# Example that allows only english usernames
# USERNAME_REGEX = r'^[A-Za-z0-9_-]+$'


class Test(BaseTest):
# This config will be used with `python setup.py test` command
pass
25 changes: 21 additions & 4 deletions mini_fiction/application.py
Expand Up @@ -32,10 +32,7 @@


def create_app():
if os.path.isfile(os.path.join(os.getcwd(), 'local_settings.py')):
os.environ.setdefault('MINIFICTION_SETTINGS', 'local_settings.Local')
else:
os.environ.setdefault('MINIFICTION_SETTINGS', 'mini_fiction.settings.Development')
select_default_settings()

app = Flask(__name__)
app.config.from_object(os.environ.get('MINIFICTION_SETTINGS'))
Expand Down Expand Up @@ -82,6 +79,26 @@ def create_app():
return app


def select_default_settings():
if os.environ.get('MINIFICTION_SETTINGS'):
return

if os.path.isfile(os.path.join(os.getcwd(), 'local_settings.py')):
os.environ.setdefault(
'MINIFICTION_SETTINGS',
'local_settings.Test' if os.environ.get('FLASK_ENV') == 'test' else 'local_settings.Local'
)

elif os.environ.get('FLASK_ENV') == 'test': # see tests/conftest.py
os.environ.setdefault('MINIFICTION_SETTINGS', 'mini_fiction.settings.Test')

elif os.environ.get('FLASK_ENV') == 'development': # uses .env file if started by mini_fiction command
os.environ.setdefault('MINIFICTION_SETTINGS', 'mini_fiction.settings.Development')

else:
os.environ.setdefault('MINIFICTION_SETTINGS', 'mini_fiction.settings.Config')


def configure_user_agent(app):
# pylint: disable=E1101

Expand Down
8 changes: 4 additions & 4 deletions mini_fiction/management/commands/checkcomments.py
Expand Up @@ -3,7 +3,7 @@

from pony import orm

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli
from mini_fiction.models import Story, StoryLocalThread, NewsItem


Expand Down Expand Up @@ -94,7 +94,7 @@ def check_comments_for(target, comments_list):
check_comments_tree(tree)


@manager.command
@cli.command()
def checkstorycomments():
orm.sql_debug(False)

Expand Down Expand Up @@ -126,7 +126,7 @@ def checkstorycomments():
story_id = story.id + 1


@manager.command
@cli.command()
def checkstorylocalcomments():
orm.sql_debug(False)

Expand All @@ -150,7 +150,7 @@ def checkstorylocalcomments():
local_id = local.id + 1


@manager.command
@cli.command()
def checknewscomments():
orm.sql_debug(False)

Expand Down
4 changes: 2 additions & 2 deletions mini_fiction/management/commands/checkstoryvoting.py
Expand Up @@ -6,11 +6,11 @@
from pony import orm
from flask import current_app

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli
from mini_fiction.models import Story


@manager.command
@cli.command()
def checkstoryvoting():
orm.sql_debug(False)

Expand Down
8 changes: 5 additions & 3 deletions mini_fiction/management/commands/checkwordscount.py
@@ -1,10 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import click
from pony import orm
from flask import current_app

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli
from mini_fiction.models import Story, Chapter


Expand Down Expand Up @@ -50,8 +51,9 @@ def story_check_words_count(story, verbose=True):
print()


@manager.option('story_ids', metavar='story_ids', nargs='*', default=(), help='Story IDs')
def checkwordscount(story_ids=None):
@cli.command()
@click.argument('story_ids', nargs=-1, type=int)
def checkwordscount(story_ids):
orm.sql_debug(False)

if story_ids:
Expand Down
11 changes: 6 additions & 5 deletions mini_fiction/management/commands/collectstatic.py
Expand Up @@ -6,9 +6,10 @@
from hashlib import sha256
from datetime import datetime

import click
from flask import current_app

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli


def collect_files(src):
Expand Down Expand Up @@ -84,10 +85,10 @@ def copyfile(src, dst, global_hash, verbose=True):
print(' (not changed)', flush=True)
return False


@manager.option('-V', '--no-verbose', dest='verbose', help='Disable verbose output', action='store_false', default=True)
@manager.option('destination', metavar='path/to/static', nargs='?', help='Target directory (default: STATIC_ROOT option)')
def collectstatic(verbose=True, destination=None):
@cli.command()
@click.option('-v/-V', '--verbose/--no-verbose', default=True)
@click.argument('destination', nargs=1, required=False)
def collectstatic(verbose, destination):
modulestatic = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname((__file__)))), 'static')
assert os.path.isdir(modulestatic)
if verbose:
Expand Down
4 changes: 2 additions & 2 deletions mini_fiction/management/commands/createsuperuser.py
Expand Up @@ -6,10 +6,10 @@
from pony.orm import db_session

from mini_fiction.models import Author
from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli


@manager.command
@cli.command()
def createsuperuser():
username = input('Username: ')
email = input('Email address: ')
Expand Down
14 changes: 8 additions & 6 deletions mini_fiction/management/commands/dumpdb.py
Expand Up @@ -3,17 +3,19 @@

import time

import click
from pony import orm

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli
from mini_fiction.utils.misc import timedelta_format


@manager.option('-s', '--silent', dest='silent', help='Don\'t print progress bar to console', action='store_true')
@manager.option('-c', '--compression', dest='gzip_compression', type=int, default=0, choices=list(range(10)), help='Use gzip compression for files')
@manager.option('entities_list', metavar='entity_name', nargs='*', default=(), help='Names of entities that will be dumped (lowercase, all by default)')
@manager.option('dirpath', metavar='output_directory', help='Directory where dump will be saved')
def dumpdb(dirpath, entities_list, gzip_compression=0, silent=False):
@cli.command()
@click.option('-s', '--silent', 'silent', help='Don\'t print progress bar to console', is_flag=True)
@click.option('-c', '--compression', 'gzip_compression', type=click.IntRange(0, 9), default=0, help='Use gzip compression for files')
@click.argument('dirpath')
@click.argument('entities_list', nargs=-1)
def dumpdb(dirpath, entities_list, gzip_compression, silent):
from mini_fiction.dumpload import dumpdb_console as cmd
orm.sql_debug(False)
tm = time.time()
Expand Down
4 changes: 2 additions & 2 deletions mini_fiction/management/commands/initsphinx.py
Expand Up @@ -10,10 +10,10 @@
from mini_fiction.models import Story, Chapter


from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli


@manager.command
@cli.command()
def initsphinx():
if current_app.config.get('SPHINX_DISABLED'):
print('Please set SPHINX_DISABLED = False before initsphinx.', file=sys.stderr)
Expand Down
12 changes: 7 additions & 5 deletions mini_fiction/management/commands/loaddb.py
Expand Up @@ -3,16 +3,18 @@

import time

import click
from pony import orm

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli
from mini_fiction.utils.misc import timedelta_format


@manager.option('-s', '--silent', dest='silent', help='Don\'t print progress bar to console', action='store_true')
@manager.option('-C', '--only-create', dest='only_create', help='Only create non-existent objects', action='store_true')
@manager.option('pathlist', metavar='input_directory', nargs='+', help='Directories or files with dump data')
def loaddb(pathlist, silent=False, only_create=False):
@cli.command()
@click.option('-s', '--silent', 'silent', help='Don\'t print progress bar to console', is_flag=True)
@click.option('-C', '--only-create', 'only_create', help='Only create non-existent objects', is_flag=True)
@click.argument('pathlist', nargs=-1, required=True)
def loaddb(pathlist, silent, only_create):
from mini_fiction.dumpload import loaddb_console as cmd
orm.sql_debug(False)
tm = time.time()
Expand Down
8 changes: 5 additions & 3 deletions mini_fiction/management/commands/seed.py
@@ -1,13 +1,15 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import click
from pony import orm

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli


@manager.option('-s', '--silent', dest='silent', help='Don\'t print progress bar to console', action='store_true')
def seed(silent=False):
@cli.command()
@click.option('-s', '--silent', 'silent', help='Don\'t print progress bar to console', is_flag=True)
def seed(silent):
from mini_fiction import fixtures
orm.sql_debug(False)
fixtures.seed(verbosity=1 if silent else 2, only_create=True)
10 changes: 6 additions & 4 deletions mini_fiction/management/commands/sendtestemail.py
@@ -1,19 +1,21 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import click
from pony.orm import db_session
from flask import current_app

from mini_fiction.utils.mail import sendmail
from mini_fiction.utils.misc import render_nonrequest_template

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli


@manager.option('-e', '--eager', dest='eager', help='Don\'t use Celery for delayed sending', action='store_true')
@manager.option('recipients', metavar='recipients', nargs='+', default=(), help='Recipient addresses')
@cli.command()
@click.option('-e', '--eager', 'eager', help='Don\'t use Celery for delayed sending', is_flag=True)
@click.argument('recipients', nargs=-1, required=True)
@db_session
def sendtestemail(recipients, eager=False):
def sendtestemail(recipients, eager):
kwargs = {
'to': recipients,
'subject': render_nonrequest_template('email/test_subject.txt'),
Expand Down
11 changes: 8 additions & 3 deletions mini_fiction/management/commands/shell.py
@@ -1,15 +1,20 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from flask import current_app
from pony.orm import db_session

from mini_fiction.management.manager import manager
from mini_fiction.management.manager import cli


@manager.command
@cli.command()
def shell():
import code
import mini_fiction
from mini_fiction import models
with db_session:
code.interact(local={'mini_fiction': mini_fiction, 'app': manager.app, 'm': models})
code.interact(local={
'mini_fiction': mini_fiction,
'app': current_app._get_current_object(),
'm': models,
})

0 comments on commit ac2c9ce

Please sign in to comment.