Merge branch 'master' into bootstrap4

master
Kirill Malev 6 years ago committed by GitHub
commit d4175ad7fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      README.rst
  2. 3
      cookiecutter.json
  3. 35
      hooks/post_gen_project.py
  4. 10
      tasks.py
  5. 5
      {{cookiecutter.app_name}}/.env.example
  6. 6
      {{cookiecutter.app_name}}/.gitignore
  7. 60
      {{cookiecutter.app_name}}/Pipfile
  8. 22
      {{cookiecutter.app_name}}/README.rst
  9. 7
      {{cookiecutter.app_name}}/autoapp.py
  10. 22
      {{cookiecutter.app_name}}/package.json
  11. 4
      {{cookiecutter.app_name}}/requirements/dev.txt
  12. 7
      {{cookiecutter.app_name}}/requirements/prod.txt
  13. 3
      {{cookiecutter.app_name}}/tests/conftest.py
  14. 11
      {{cookiecutter.app_name}}/tests/settings.py
  15. 19
      {{cookiecutter.app_name}}/tests/test_config.py
  16. 3
      {{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py
  17. 56
      {{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py

@ -25,6 +25,7 @@ Features
- Bootstrap 3 and Font Awesome 4 with starter templates
- Flask-SQLAlchemy with basic User model
- Easy database migrations with Flask-Migrate
- Configuration in environment variables, as per `The Twelve-Factor App <https://12factor.net/config>`_
- Flask-WTForms with login and registration forms
- Flask-Login for authentication
- Flask-Bcrypt for password hashing

@ -4,5 +4,6 @@
"github_username": "sloria",
"project_name": "My Flask App",
"app_name": "myflaskapp",
"project_short_description": "A flasky app."
"project_short_description": "A flasky app.",
"use_pipenv": ["no", "yes"]
}

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Post gen hook to ensure that the generated project
hase only one package managment, either pipenv or pip."""
import os
import shutil
import sys
def clean_extra_package_managment_files():
"""Removes either requirements files and folderor the Pipfile."""
use_pipenv = '{{cookiecutter.use_pipenv}}'
to_delete = []
if use_pipenv == 'yes':
to_delete = to_delete + ['requirements.txt', 'requirements']
else:
to_delete.append('Pipfile')
try:
for file_or_dir in to_delete:
if os.path.isfile(file_or_dir):
os.remove(file_or_dir)
else:
shutil.rmtree(file_or_dir)
sys.exit(0)
except OSError as e:
sys.stdout.write(
'While attempting to remove file(s) an error occurred'
)
sys.stdout.write('Error: {}'.format(e))
if __name__ == '__main__':
clean_extra_package_managment_files()

@ -13,7 +13,6 @@ with open(os.path.join(HERE, 'cookiecutter.json'), 'r') as fp:
COOKIECUTTER_SETTINGS = json.load(fp)
# Match default value of app_name from cookiecutter.json
COOKIE = os.path.join(HERE, COOKIECUTTER_SETTINGS['app_name'])
AUTOAPP = os.path.join(COOKIE, 'autoapp.py')
REQUIREMENTS = os.path.join(COOKIE, 'requirements', 'dev.txt')
@ -42,7 +41,8 @@ def clean(ctx):
def _run_flask_command(ctx, command):
ctx.run('FLASK_APP={0} flask {1}'.format(AUTOAPP, command), echo=True)
os.chdir(COOKIE)
ctx.run('flask {0}'.format(command), echo=True)
@task(pre=[clean, build])
@ -52,12 +52,14 @@ def test(ctx):
echo=True)
_run_npm_command(ctx, 'run lint')
os.chdir(COOKIE)
shutil.copyfile(os.path.join(COOKIE, '.env.example'),
os.path.join(COOKIE, '.env'))
_run_flask_command(ctx, 'lint')
_run_flask_command(ctx, 'test')
@task
def readme(ctx, browse=False):
ctx.run("rst2html.py README.rst > README.html")
ctx.run('rst2html.py README.rst > README.html')
if browse:
webbrowser.open_new_tab('README.html')

@ -0,0 +1,5 @@
# Environment variable overrides for local development
FLASK_APP=autoapp.py
FLASK_ENV=development
DATABASE_URL="sqlite:////tmp/dev.db"
SECRET_KEY="not-so-secret"

@ -51,3 +51,9 @@ env/
# webpack-built files
/{{cookiecutter.app_name}}/static/build/
/{{cookiecutter.app_name}}/webpack/manifest.json
# Configuration
.env
# Development database
*.db

@ -0,0 +1,60 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
# Flask
Flask = "==1.0.2"
MarkupSafe = "==1.0"
Werkzeug = "==0.14.1"
Jinja2 = "==2.10"
itsdangerous = "==0.24"
click = ">=5.0"
# Database
Flask-SQLAlchemy = "==2.3.2"
psycopg2 = "==2.7.5"
SQLAlchemy = "==1.2.8"
# Migrations
Flask-Migrate = "==2.2.0"
# Forms
Flask-WTF = "==0.14.2"
WTForms = "==2.2.1"
# Deployment
gunicorn = ">=19.1.1"
# Webpack
flask-webpack = "==0.1.0"
# Auth
Flask-Login = "==0.4.1"
Flask-Bcrypt = "==0.7.1"
# Caching
Flask-Caching = ">=1.0.0"
# Debug toolbar
Flask-DebugToolbar = "==0.10.1"
# Environment variable parsing
environs = "==4.0.0"
[dev-packages]
# Testing
pytest = "==3.6.1"
WebTest = "==2.0.30"
factory-boy = "==2.11.*"
# Lint and code style
flake8 = "==3.5.0"
flake8-blind-except = "==0.1.1"
flake8-debugger = "==3.1.0"
flake8-docstrings = "==1.3.0"
flake8-isort = "==2.5"
flake8-quotes = "==1.0.0"
isort = "==4.3.4"
pep8-naming = "==0.7.0"

@ -8,29 +8,21 @@
Quickstart
----------
First, set your app's secret key as an environment variable. For example,
add the following to ``.bashrc`` or ``.bash_profile``.
.. code-block:: bash
export {{cookiecutter.app_name | upper}}_SECRET='something-really-secret'
Run the following commands to bootstrap your environment ::
git clone https://github.com/{{cookiecutter.github_username}}/{{cookiecutter.app_name}}
cd {{cookiecutter.app_name}}
{%- if cookiecutter.use_pipenv == "yes" %}
pipenv install --dev
{%- else %}
pip install -r requirements/dev.txt
{%- endif %}
cp .env.example .env
npm install
npm start # run the webpack dev server and flask server using concurrently
You will see a pretty welcome screen.
In general, before running shell commands, set the ``FLASK_APP`` and
``FLASK_DEBUG`` environment variables ::
export FLASK_APP=autoapp.py
export FLASK_DEBUG=1
Once you have installed your DBMS, run the following to create your app's
database tables and perform the initial migration ::
@ -45,12 +37,14 @@ Deployment
To deploy::
export FLASK_ENV=production
export FLASK_DEBUG=0
export DATABASE_URL="<YOUR DATABASE URL>"
npm run build # build assets with webpack
flask run # start the flask server
In your production environment, make sure the ``FLASK_DEBUG`` environment
variable is unset or is set to ``0``, so that ``ProdConfig`` is used.
variable is unset or is set to ``0``.
Shell

@ -1,10 +1,5 @@
# -*- coding: utf-8 -*-
"""Create an application instance."""
from flask.helpers import get_debug_flag
from {{cookiecutter.app_name}}.app import create_app
from {{cookiecutter.app_name}}.settings import DevConfig, ProdConfig
CONFIG = DevConfig if get_debug_flag() else ProdConfig
app = create_app(CONFIG)
app = create_app()

@ -6,7 +6,7 @@
"build": "NODE_ENV=production webpack --progress --colors -p",
"start": "concurrently -n \"WEBPACK,FLASK\" -c \"bgBlue.bold,bgMagenta.bold\" \"npm run webpack-dev-server\" \"npm run flask-server\"",
"webpack-dev-server": "NODE_ENV=debug webpack-dev-server --port 2992 --hot --inline",
"flask-server": "FLASK_APP=$PWD/autoapp.py FLASK_DEBUG=1 flask run",
"flask-server": "flask run",
"lint": "eslint \"assets/js/*.js\""
},
"repository": {
@ -28,25 +28,25 @@
},
"devDependencies": {
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-eslint": "^9.0.0",
"babel-loader": "^7.0.0",
"babel-preset-env": "^1.6.0",
"concurrently": "^3.5.0",
"css-loader": "^0.28.4",
"eslint": "^3.19.0",
"eslint-config-airbnb-base": "^11.2.0",
"concurrently": "^4.0.1",
"css-loader": "^1.0.0",
"eslint": "^5.3.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.3.0",
"extract-text-webpack-plugin": "^2.1.2",
"file-loader": "^0.11.2",
"file-loader": "^2.0.0",
"font-awesome-webpack": "0.0.5-beta.2",
"less": "^2.7.2",
"less": "^3.8.0",
"less-loader": "^4.0.4",
"manifest-revision-webpack-plugin": "^0.4.0",
"raw-loader": "^0.5.1",
"style-loader": "^0.18.2",
"url-loader": "^0.5.9",
"style-loader": "^0.23.0",
"url-loader": "^1.0.1",
"webpack": "^2.6.1",
"webpack-dev-server": "^2.4.5",
"webpack-dev-server": "^3.1.5",
"sync-exec": "^0.6.2"
}
}

@ -2,8 +2,8 @@
-r prod.txt
# Testing
pytest==3.6.1
WebTest==2.0.29
pytest==3.7.4
WebTest==2.0.30
factory-boy==2.11.1
# Lint and code style

@ -11,10 +11,10 @@ click>=5.0
# Database
Flask-SQLAlchemy==2.3.2
psycopg2==2.7.5
SQLAlchemy==1.2.8
SQLAlchemy==1.2.11
# Migrations
Flask-Migrate==2.2.0
Flask-Migrate==2.2.1
# Forms
Flask-WTF==0.14.2
@ -35,3 +35,6 @@ Flask-Caching>=1.0.0
# Debug toolbar
Flask-DebugToolbar==0.10.1
# Environment variable parsing
environs==4.0.0

@ -6,7 +6,6 @@ from webtest import TestApp
from {{cookiecutter.app_name}}.app import create_app
from {{cookiecutter.app_name}}.database import db as _db
from {{cookiecutter.app_name}}.settings import TestConfig
from .factories import UserFactory
@ -14,7 +13,7 @@ from .factories import UserFactory
@pytest.fixture
def app():
"""An application for the tests."""
_app = create_app(TestConfig)
_app = create_app('tests.settings')
ctx = _app.test_request_context()
ctx.push()

@ -0,0 +1,11 @@
"""Settings module for test app."""
ENV = 'development'
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite://'
SECRET_KEY = 'not-so-secret-in-tests'
BCRYPT_LOG_ROUNDS = 4 # For faster tests; needs at least 4 to avoid "ValueError: Invalid rounds"
DEBUG_TB_ENABLED = False
CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc.
SQLALCHEMY_TRACK_MODIFICATIONS = False
WEBPACK_MANIFEST_PATH = 'webpack/manifest.json'
WTF_CSRF_ENABLED = False # Allows form testing

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
"""Test configs."""
from {{cookiecutter.app_name}}.app import create_app
from {{cookiecutter.app_name}}.settings import DevConfig, ProdConfig
def test_production_config():
"""Production config."""
app = create_app(ProdConfig)
assert app.config['ENV'] == 'prod'
assert app.config['DEBUG'] is False
assert app.config['DEBUG_TB_ENABLED'] is False
def test_dev_config():
"""Development config."""
app = create_app(DevConfig)
assert app.config['ENV'] == 'dev'
assert app.config['DEBUG'] is True

@ -4,10 +4,9 @@ from flask import Flask, render_template
from {{cookiecutter.app_name}} import commands, public, user
from {{cookiecutter.app_name}}.extensions import bcrypt, cache, csrf_protect, db, debug_toolbar, login_manager, migrate, webpack
from {{cookiecutter.app_name}}.settings import ProdConfig
def create_app(config_object=ProdConfig):
def create_app(config_object='{{cookiecutter.app_name}}.settings'):
"""An application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.
:param config_object: The configuration object to use.

@ -1,49 +1,23 @@
# -*- coding: utf-8 -*-
"""Application configuration."""
import os
"""Application configuration.
Most configuration is set via environment variables.
class Config(object):
"""Base configuration."""
For local development, use a .env file to set
environment variables.
"""
from environs import Env
SECRET_KEY = os.environ.get('{{cookiecutter.app_name | upper}}_SECRET', 'secret-key') # TODO: Change me
APP_DIR = os.path.abspath(os.path.dirname(__file__)) # This directory
PROJECT_ROOT = os.path.abspath(os.path.join(APP_DIR, os.pardir))
BCRYPT_LOG_ROUNDS = 13
DEBUG_TB_ENABLED = False # Disable Debug toolbar
env = Env()
env.read_env()
ENV = env.str('FLASK_ENV', default='production')
DEBUG = ENV == 'development'
SQLALCHEMY_DATABASE_URI = env.str('DATABASE_URL')
SECRET_KEY = env.str('SECRET_KEY')
BCRYPT_LOG_ROUNDS = env.int('BCRYPT_LOG_ROUNDS', default=13)
DEBUG_TB_ENABLED = DEBUG
DEBUG_TB_INTERCEPT_REDIRECTS = False
CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc.
SQLALCHEMY_TRACK_MODIFICATIONS = False
WEBPACK_MANIFEST_PATH = 'webpack/manifest.json'
class ProdConfig(Config):
"""Production configuration."""
ENV = 'prod'
DEBUG = False
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/example' # TODO: Change me
DEBUG_TB_ENABLED = False # Disable Debug toolbar
class DevConfig(Config):
"""Development configuration."""
ENV = 'dev'
DEBUG = True
DB_NAME = 'dev.db'
# Put the db file in project root
DB_PATH = os.path.join(Config.PROJECT_ROOT, DB_NAME)
SQLALCHEMY_DATABASE_URI = 'sqlite:///{0}'.format(DB_PATH)
DEBUG_TB_ENABLED = True
CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc.
class TestConfig(Config):
"""Test configuration."""
TESTING = True
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite://'
BCRYPT_LOG_ROUNDS = 4 # For faster tests; needs at least 4 to avoid "ValueError: Invalid rounds"
WTF_CSRF_ENABLED = False # Allows form testing

Loading…
Cancel
Save