├── .coveragerc ├── .dockerignore ├── .editorconfig ├── .gitignore ├── .travis.yml ├── AUTHORS ├── CHANGES ├── CONTRIBUTING.rst ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── README.rst ├── RELEASE-NOTES.rst ├── docker-compose.yml ├── docs ├── Makefile ├── _templates │ └── sidebarintro.html ├── api.rst ├── conf.py ├── contributing.rst ├── index.rst ├── installation.rst ├── tut_app.rst ├── tut_celery.rst ├── tut_ext.rst ├── tut_package.rst └── tutorial.rst ├── examples ├── manage.py ├── myapp │ ├── __init__.py │ ├── app.py │ ├── celery.py │ ├── cli.py │ ├── config.py │ └── wsgi.py ├── myexts │ ├── __init__.py │ └── sqlalchemy.py ├── mymodule │ ├── __init__.py │ ├── cli.py │ ├── config.py │ ├── models.py │ ├── tasks.py │ ├── templates │ │ ├── mymodule.html │ │ └── mymodule_base.html │ └── views.py └── setup.py ├── flask_appfactory ├── __init__.py ├── app.py ├── celery.py ├── cli.py ├── ext │ ├── __init__.py │ └── jinja2.py └── version.py ├── pytest.ini ├── requirements.devel.txt ├── requirements.latest.txt ├── requirements.lowest.txt ├── run-tests.sh ├── setup.cfg ├── setup.py ├── tests ├── simplemodule │ ├── __init__.py │ ├── cli.py │ ├── config.py │ ├── tasks.py │ ├── templates │ │ └── test.html │ └── views.py ├── simplemodule2 │ ├── __init__.py │ ├── templates │ │ └── test.html │ └── views.py ├── test_app.py ├── test_celery.py ├── test_cli.py └── test_ext.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | ## 2 | ## This file is part of Flask-AppFactory 3 | ## Copyright (C) 2014 CERN. 4 | ## 5 | ## Flask-AppFactory is free software; you can redistribute it and/or 6 | ## modify it under the terms of the GNU General Public License as 7 | ## published by the Free Software Foundation; either version 2 of the 8 | ## License, or (at your option) any later version. 9 | ## 10 | ## Flask-AppFactory is distributed in the hope that it will be useful, but 11 | ## WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | ## General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with Flask-AppFactory; if not, write to the Free Software Foundation, 17 | ## Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | ## 19 | ## In applying this licence, CERN does not waive the privileges and immunities 20 | ## granted to it by virtue of its status as an Intergovernmental Organization 21 | ## or submit itself to any jurisdiction. 22 | 23 | [run] 24 | source = flask_appfactory -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | *.pyc 3 | __pycache__/ 4 | .tox 5 | .cache 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | charset = utf-8 9 | 10 | # Python files 11 | [*.py] 12 | indent_size = 4 13 | # isort plugin configuration 14 | multi_line_output = 2 15 | default_section = THIRDPARTY 16 | 17 | # RST files (used by sphinx) 18 | [*.rst] 19 | indent_size = 4 20 | 21 | # CSS, HTML, JS, JSON, YML 22 | [*.{css,html,js,json,yml}] 23 | indent_size = 2 24 | 25 | # Matches the exact files either package.json or .travis.yml 26 | [{package.json,.travis.yml}] 27 | indent_size = 2 28 | 29 | # Dockerfile 30 | [Dockerfile] 31 | indent_size = 4 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # This file is part of Flask-AppFactory 2 | # Copyright (C) 2015 CERN. 3 | # 4 | # Flask-AppFactory is free software; you can redistribute it and/or 5 | # modify it under the terms of the Revised BSD License; see LICENSE 6 | # file for more details. 7 | 8 | sudo: false 9 | 10 | language: python 11 | 12 | python: 13 | - "2.7" 14 | - "3.3" 15 | - "3.4" 16 | 17 | env: 18 | - REQUIREMENTS=devel 19 | - REQUIREMENTS=latest 20 | - REQUIREMENTS=lowest 21 | 22 | cache: 23 | - pip 24 | 25 | install: 26 | # Install test dependencies 27 | - "travis_retry pip install coveralls pep257 Sphinx twine wheel" 28 | - "travis_retry pip install pytest pytest-pep8 pytest-cov pytest-cache" 29 | - "travis_retry pip install -r requirements.${REQUIREMENTS}.txt" 30 | - "travis_retry pip install -e ." 31 | 32 | script: 33 | - pep257 flask_appfactory 34 | - "sphinx-build -qnNW docs docs/_build/html" 35 | - python setup.py test 36 | - "sphinx-build -qnNW -b doctest docs docs/_build/doctest" 37 | 38 | after_success: 39 | - coveralls 40 | 41 | notifications: 42 | email: false 43 | 44 | deploy: 45 | provider: pypi 46 | user: lnielsen 47 | password: 48 | secure: DUpiRtaTs2bXcxTbs7k+B7JzEsSIQzCdeGOHr99D/zYg5PvSX8Y4dqerdF5OgutseqWmed4k9ij4iHCMjkMwoU1qqznPwEGnvAiCbNIHshy4DzJSkSKva+6YHpEs8qNP0wO4qub/LsI7kcZ/ZXmiC98pjAe8OjlJsg2nVzgJt9s= 49 | distributions: "sdist bdist_wheel" 50 | on: 51 | tags: true 52 | python: "2.7" 53 | condition: $REQUIREMENTS = latest 54 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | Contact us at `info@inveniosoftware.org `_ 5 | 6 | * Lars Holm Nielsen 7 | * Jiri Kuncar 8 | * Tibor Simko 9 | * Yoan Blanc 10 | * Marco Neumann 11 | * Esteban J. G. Gabancho 12 | * Samuele Kaplun 13 | * Konstantinos Ntemagkos 14 | * Jan Aage Lavik 15 | * Ivan Masár 16 | * Adrian Tudor Panescu 17 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | Changes 2 | ======= 3 | 4 | Version 0.2.1 (released 2015-08-18) 5 | 6 | - Fixes issue with flask_appfactory.ext not being installed due to missing 7 | entry in setup.py. 8 | 9 | - Adds better documentation about API changes. 10 | 11 | Version 0.2.0 (released 2015-08-17) 12 | 13 | - Adds support for creating Celery applications with Flask-AppFactory. 14 | 15 | - Adds support for using a custom Flask application class. 16 | 17 | - Fixes minor issues with configuration registry loading order 18 | 19 | - Increases test coverage to 100%. 20 | 21 | Version 0.1.0 (released 2015-08-03) 22 | 23 | - Initial public release. 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Bug reports, feature requests, and other contributions are welcome. 5 | If you find a demonstrable problem that is caused by the code of this 6 | library, please: 7 | 8 | 1. Search for `already reported problems 9 | `_. 10 | 2. Check if the issue has been fixed or is still reproducible on the 11 | latest `master` branch. 12 | 3. Create an issue with **a test case**. 13 | 14 | If you create a feature branch, you can run the tests to ensure everything is 15 | operating correctly: 16 | 17 | .. code-block:: console 18 | 19 | $ ./run-tests.sh 20 | 21 | You can also test your feature branch using Docker: 22 | 23 | .. code-block:: console 24 | 25 | $ docker-compose build 26 | $ docker-compose run --rm web /code/run-tests.sh 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This file is part of Flask-AppFactory 2 | # Copyright (C) 2015 CERN. 3 | # 4 | # Flask-AppFactory is free software; you can redistribute it and/or modify it under 5 | # the terms of the Revised BSD License; see LICENSE file for more details. 6 | 7 | # Use Python-2.7: 8 | FROM python:2.7 9 | 10 | # Install some prerequisites ahead of `setup.py` in order to profit 11 | # from the docker build cache: 12 | RUN pip install Flask \ 13 | Flask-CLI \ 14 | Flask-Registry \ 15 | Sphinx \ 16 | Werkzeug \ 17 | click \ 18 | coveralls \ 19 | ipython \ 20 | itsdangerous \ 21 | pep257 \ 22 | pytest \ 23 | pytest-cache \ 24 | pytest-cov \ 25 | pytest-pep8 26 | 27 | # Add sources to `code` and work there: 28 | WORKDIR /code 29 | ADD . /code 30 | 31 | # Install flask-appfactory: 32 | RUN pip install -e . 33 | 34 | # Run container as user `flask-appfactory` with UID `1000`, which should match 35 | # current host user in most situations: 36 | RUN adduser --uid 1000 --disabled-password --gecos '' flaskappfactory && \ 37 | chown -R flaskappfactory:flaskappfactory /code 38 | 39 | # Run test suite instead of starting the application: 40 | USER flaskappfactory 41 | CMD ["python", "setup.py", "test"] 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Flask-AppFactory is free software; you can redistribute it and/or modify it 2 | under the terms of the Revised BSD License quoted below. 3 | 4 | Copyright (C) 2015 CERN. 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are 10 | met: 11 | 12 | * Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in the 17 | documentation and/or other materials provided with the distribution. 18 | 19 | * Neither the name of the copyright holder nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 30 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 32 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 33 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 34 | DAMAGE. 35 | 36 | In applying this license, CERN does not waive the privileges and immunities 37 | granted to it by virtue of its status as an Intergovernmental Organization or 38 | submit itself to any jurisdiction. 39 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | include LICENSE AUTHORS CHANGES README.rst 11 | include .coveragerc run-tests.sh 12 | include docs/*.rst docs/*.py docs/Makefile 13 | include tests/*.py 14 | include examples/*.py 15 | recursive-include docs/_themes *.py *.css *.css_t *.conf *.html README 16 | recursive-include docs/_templates *.html 17 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Flask-AppFactory 3 | ================== 4 | 5 | .. image:: https://travis-ci.org/inveniosoftware/flask-appfactory.svg?branch=master 6 | :target: https://travis-ci.org/inveniosoftware/flask-appfactory 7 | .. image:: https://coveralls.io/repos/inveniosoftware/flask-appfactory/badge.svg?branch=master 8 | :target: https://coveralls.io/r/inveniosoftware/flask-appfactory 9 | .. image:: https://pypip.in/v/flask-appfactory/badge.svg 10 | :target: https://crate.io/packages/flask-appfactory/ 11 | .. image:: https://pypip.in/d/flask-appfactory/badge.svg 12 | :target: https://crate.io/packages/flask-appfactory/ 13 | 14 | Flask-AppFactory is an dynamic application loader. 15 | 16 | It allows you to build reusable modules that can be easily be assembled into 17 | full Flask applications using this loader. Each reusable module can provide 18 | default configuration, blueprints and command line interface. 19 | 20 | Installation 21 | ============ 22 | Flask-AppFactory is on PyPI so all you need is: :: 23 | 24 | pip install Flask-AppFactory 25 | 26 | Documentation 27 | ============= 28 | Documentation is available at or can be build using Sphinx: :: 29 | 30 | pip install Sphinx 31 | python setup.py build_sphinx 32 | 33 | Testing 34 | ======= 35 | Running the tests are as simple as: :: 36 | 37 | python setup.py test 38 | 39 | or (to also show test coverage) :: 40 | 41 | ./run-tests.sh 42 | -------------------------------------------------------------------------------- /RELEASE-NOTES.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Flask-AppFactory v0.2.1 3 | ========================= 4 | 5 | Flask-AppFactory v0.2.1 was released on August 18, 2015. 6 | 7 | About 8 | ----- 9 | 10 | Flask-AppFactory is an application loader which assembles an Flask 11 | applications from individually released modules based on configuration. 12 | 13 | Installation 14 | ------------ 15 | 16 | $ pip install flask-appfactory 17 | 18 | What's new 19 | ---------- 20 | 21 | - Fixes issue with flask_appfactory.ext not being installed due to missing 22 | entry in setup.py. 23 | 24 | - Adds better documentation about API changes. 25 | 26 | Documentation 27 | ------------- 28 | 29 | http://flask-appfactory.readthedocs.org/en/v0.2.1 30 | 31 | Homepage 32 | -------- 33 | 34 | https://github.com/inveniosoftware/flask-appfactory 35 | 36 | Happy hacking and thanks for flying Flask-AppFactory. 37 | 38 | | Invenio Development Team 39 | | Email: info@inveniosoftware.org 40 | | Twitter: http://twitter.com/inveniosoftware 41 | | GitHub: http://github.com/inveniosoftware 42 | | URL: http://inveniosoftware.org 43 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # This file is part of Flask-AppFactory 2 | # Copyright (C) 2015 CERN. 3 | # 4 | # Flask-AppFactory is free software; you can redistribute it and/or modify it under 5 | # the terms of the Revised BSD License; see LICENSE file for more details. 6 | 7 | web: 8 | build: . 9 | command: python setup.py test 10 | volumes: 11 | - .:/code 12 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ## 3 | ## This file is part of Flask-AppFactory 4 | ## Copyright (C) 2014 CERN. 5 | ## 6 | ## Flask-AppFactory is free software; you can redistribute it and/or 7 | ## modify it under the terms of the GNU General Public License as 8 | ## published by the Free Software Foundation; either version 2 of the 9 | ## License, or (at your option) any later version. 10 | ## 11 | ## Flask-AppFactory is distributed in the hope that it will be useful, but 12 | ## WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | ## General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with Flask-AppFactory; if not, write to the Free Software Foundation, 18 | ## Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 19 | ## 20 | ## In applying this licence, CERN does not waive the privileges and immunities 21 | ## granted to it by virtue of its status as an Intergovernmental Organization 22 | ## or submit itself to any jurisdiction. 23 | 24 | # You can set these variables from the command line. 25 | SPHINXOPTS = 26 | SPHINXBUILD = sphinx-build 27 | PAPER = 28 | BUILDDIR = _build 29 | 30 | # User-friendly check for sphinx-build 31 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 32 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 33 | endif 34 | 35 | # Internal variables. 36 | PAPEROPT_a4 = -D latex_paper_size=a4 37 | PAPEROPT_letter = -D latex_paper_size=letter 38 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 39 | # the i18n builder cannot share the environment and doctrees with the others 40 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 41 | 42 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 43 | 44 | help: 45 | @echo "Please use \`make ' where is one of" 46 | @echo " html to make standalone HTML files" 47 | @echo " dirhtml to make HTML files named index.html in directories" 48 | @echo " singlehtml to make a single large HTML file" 49 | @echo " pickle to make pickle files" 50 | @echo " json to make JSON files" 51 | @echo " htmlhelp to make HTML files and a HTML help project" 52 | @echo " qthelp to make HTML files and a qthelp project" 53 | @echo " devhelp to make HTML files and a Devhelp project" 54 | @echo " epub to make an epub" 55 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 56 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 57 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 58 | @echo " text to make text files" 59 | @echo " man to make manual pages" 60 | @echo " texinfo to make Texinfo files" 61 | @echo " info to make Texinfo files and run them through makeinfo" 62 | @echo " gettext to make PO message catalogs" 63 | @echo " changes to make an overview of all changed/added/deprecated items" 64 | @echo " xml to make Docutils-native XML files" 65 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 66 | @echo " linkcheck to check all external links for integrity" 67 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 68 | 69 | clean: 70 | rm -rf $(BUILDDIR)/* 71 | 72 | html: 73 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 74 | @echo 75 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 76 | 77 | coverage: 78 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 79 | @echo 80 | @echo "Build finished. The coverage pages are in $(BUILDDIR)/coverage/python.txt." 81 | 82 | dirhtml: 83 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 84 | @echo 85 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 86 | 87 | singlehtml: 88 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 89 | @echo 90 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 91 | 92 | pickle: 93 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 94 | @echo 95 | @echo "Build finished; now you can process the pickle files." 96 | 97 | json: 98 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 99 | @echo 100 | @echo "Build finished; now you can process the JSON files." 101 | 102 | htmlhelp: 103 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 104 | @echo 105 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 106 | ".hhp project file in $(BUILDDIR)/htmlhelp." 107 | 108 | qthelp: 109 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 110 | @echo 111 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 112 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 113 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask-AppFactory.qhcp" 114 | @echo "To view the help file:" 115 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask-AppFactory.qhc" 116 | 117 | devhelp: 118 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 119 | @echo 120 | @echo "Build finished." 121 | @echo "To view the help file:" 122 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask-AppFactory" 123 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Flask-AppFactory" 124 | @echo "# devhelp" 125 | 126 | epub: 127 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 128 | @echo 129 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 130 | 131 | latex: 132 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 133 | @echo 134 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 135 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 136 | "(use \`make latexpdf' here to do that automatically)." 137 | 138 | latexpdf: 139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 140 | @echo "Running LaTeX files through pdflatex..." 141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 143 | 144 | latexpdfja: 145 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 146 | @echo "Running LaTeX files through platex and dvipdfmx..." 147 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 148 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 149 | 150 | text: 151 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 152 | @echo 153 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 154 | 155 | man: 156 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 157 | @echo 158 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 159 | 160 | texinfo: 161 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 162 | @echo 163 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 164 | @echo "Run \`make' in that directory to run these through makeinfo" \ 165 | "(use \`make info' here to do that automatically)." 166 | 167 | info: 168 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 169 | @echo "Running Texinfo files through makeinfo..." 170 | make -C $(BUILDDIR)/texinfo info 171 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 172 | 173 | gettext: 174 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 175 | @echo 176 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 177 | 178 | changes: 179 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 180 | @echo 181 | @echo "The overview file is in $(BUILDDIR)/changes." 182 | 183 | linkcheck: 184 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 185 | @echo 186 | @echo "Link check complete; look for any errors in the above output " \ 187 | "or in $(BUILDDIR)/linkcheck/output.txt." 188 | 189 | doctest: 190 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 191 | @echo "Testing of doctests in the sources finished, look at the " \ 192 | "results in $(BUILDDIR)/doctest/output.txt." 193 | 194 | xml: 195 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 196 | @echo 197 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 198 | 199 | pseudoxml: 200 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 201 | @echo 202 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 203 | -------------------------------------------------------------------------------- /docs/_templates/sidebarintro.html: -------------------------------------------------------------------------------- 1 |

About

2 |

3 | Flask-AppFactory is an extension for Flask that CHANGEME 4 |

5 |

Useful Links

6 | 12 | 13 | Fork me on GitHub -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | API Docs 4 | ======== 5 | 6 | 7 | Application factory 8 | ------------------- 9 | .. automodule:: flask_appfactory.app 10 | :members: 11 | :undoc-members: 12 | 13 | CLI factory 14 | ----------- 15 | .. automodule:: flask_appfactory.cli 16 | :members: 17 | :undoc-members: 18 | 19 | Celery factory 20 | -------------- 21 | .. automodule:: flask_appfactory.celery 22 | :members: 23 | :undoc-members: 24 | 25 | Extensions 26 | ---------- 27 | 28 | .. automodule:: flask_appfactory.ext 29 | :members: 30 | :undoc-members: 31 | 32 | Jinja2 33 | ~~~~~~ 34 | 35 | .. automodule:: flask_appfactory.ext.jinja2 36 | :members: 37 | :undoc-members: 38 | 39 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Flask-AppFactory documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Jul 31 13:10:12 2015. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import shlex 18 | import re 19 | 20 | # If extensions (or modules to document with autodoc) are in another directory, 21 | # add these directories to sys.path here. If the directory is relative to the 22 | # documentation root, use os.path.abspath to make it absolute, like shown here. 23 | #sys.path.insert(0, os.path.abspath('.')) 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | #needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | 'sphinx.ext.autodoc', 35 | 'sphinx.ext.doctest', 36 | 'sphinx.ext.viewcode', 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | # templates_path = ['_templates'] 41 | 42 | # The suffix(es) of source filenames. 43 | # You can specify multiple suffix as a list of string: 44 | # source_suffix = ['.rst', '.md'] 45 | source_suffix = '.rst' 46 | 47 | # The encoding of source files. 48 | #source_encoding = 'utf-8-sig' 49 | 50 | # The master toctree document. 51 | master_doc = 'index' 52 | 53 | # General information about the project. 54 | project = u'Flask-AppFactory' 55 | copyright = u'2015, CERN' 56 | author = u'Invenio Software' 57 | 58 | # The version info for the project you're documenting, acts as replacement for 59 | # |version| and |release|, also used in various other places throughout the 60 | # built documents. 61 | # 62 | # The short X.Y version. 63 | # Get the version string. Cannot be done with import! 64 | with open(os.path.join('..', 'flask_appfactory', 'version.py'), 'rt') as f: 65 | version = re.search( 66 | '__version__\s*=\s*"(?P.*)"\n', 67 | f.read() 68 | ).group('version') 69 | 70 | # The full version, including alpha/beta/rc tags. 71 | release = version 72 | 73 | # The language for content autogenerated by Sphinx. Refer to documentation 74 | # for a list of supported languages. 75 | # 76 | # This is also used if you do content translation via gettext catalogs. 77 | # Usually you set "language" from the command line for these cases. 78 | language = None 79 | 80 | # There are two options for replacing |today|: either, you set today to some 81 | # non-false value, then it is used: 82 | #today = '' 83 | # Else, today_fmt is used as the format for a strftime call. 84 | #today_fmt = '%B %d, %Y' 85 | 86 | # List of patterns, relative to source directory, that match files and 87 | # directories to ignore when looking for source files. 88 | exclude_patterns = ['_build'] 89 | 90 | # The reST default role (used for this markup: `text`) to use for all 91 | # documents. 92 | #default_role = None 93 | 94 | # If true, '()' will be appended to :func: etc. cross-reference text. 95 | #add_function_parentheses = True 96 | 97 | # If true, the current module name will be prepended to all description 98 | # unit titles (such as .. function::). 99 | #add_module_names = True 100 | 101 | # If true, sectionauthor and moduleauthor directives will be shown in the 102 | # output. They are ignored by default. 103 | #show_authors = False 104 | 105 | # The name of the Pygments (syntax highlighting) style to use. 106 | pygments_style = 'sphinx' 107 | 108 | # A list of ignored prefixes for module index sorting. 109 | #modindex_common_prefix = [] 110 | 111 | # If true, keep warnings as "system message" paragraphs in the built documents. 112 | #keep_warnings = False 113 | 114 | # If true, `todo` and `todoList` produce output, else they produce nothing. 115 | todo_include_todos = False 116 | 117 | 118 | # -- Options for HTML output ---------------------------------------------- 119 | 120 | # The theme to use for HTML and HTML Help pages. See the documentation for 121 | # a list of builtin themes. 122 | html_theme = 'alabaster' 123 | 124 | # Theme options are theme-specific and customize the look and feel of a theme 125 | # further. For a list of options available for each theme, see the 126 | # documentation. 127 | html_theme_options = { 128 | 'description': 'Flask-AppFactory is an dynamic application loader.', 129 | 'github_user': 'inveniosoftware', 130 | 'github_repo': 'flask-appfactory', 131 | 'github_button': False, 132 | 'github_banner': True, 133 | 'show_powered_by': False, 134 | 'extra_nav_links' : { 135 | 'Flask-AppFactory@GitHub' : 'http://github.com/inveniosoftware/flask-appfactory/', 136 | 'Flask-AppFactory@PyPI' : 'http://pypi.python.org/pypi/Flask-AppFactory/', 137 | } 138 | } 139 | 140 | # Add any paths that contain custom themes here, relative to this directory. 141 | #html_theme_path = [] 142 | 143 | # The name for this set of Sphinx documents. If None, it defaults to 144 | # " v documentation". 145 | #html_title = None 146 | 147 | # A shorter title for the navigation bar. Default is the same as html_title. 148 | #html_short_title = None 149 | 150 | # The name of an image file (relative to this directory) to place at the top 151 | # of the sidebar. 152 | #html_logo = None 153 | 154 | # The name of an image file (within the static path) to use as favicon of the 155 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 156 | # pixels large. 157 | #html_favicon = None 158 | 159 | # Add any paths that contain custom static files (such as style sheets) here, 160 | # relative to this directory. They are copied after the builtin static files, 161 | # so a file named "default.css" will overwrite the builtin "default.css". 162 | #html_static_path = ['_static'] 163 | 164 | # Add any extra paths that contain custom files (such as robots.txt or 165 | # .htaccess) here, relative to this directory. These files are copied 166 | # directly to the root of the documentation. 167 | #html_extra_path = [] 168 | 169 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 170 | # using the given strftime format. 171 | #html_last_updated_fmt = '%b %d, %Y' 172 | 173 | # If true, SmartyPants will be used to convert quotes and dashes to 174 | # typographically correct entities. 175 | #html_use_smartypants = True 176 | 177 | # Custom sidebar templates, maps document names to template names. 178 | #html_sidebars = {} 179 | 180 | # Additional templates that should be rendered to pages, maps page names to 181 | # template names. 182 | #html_additional_pages = {} 183 | 184 | # If false, no module index is generated. 185 | #html_domain_indices = True 186 | 187 | # If false, no index is generated. 188 | #html_use_index = True 189 | 190 | # If true, the index is split into individual pages for each letter. 191 | #html_split_index = False 192 | 193 | # If true, links to the reST sources are added to the pages. 194 | #html_show_sourcelink = True 195 | 196 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 197 | #html_show_sphinx = True 198 | 199 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 200 | #html_show_copyright = True 201 | 202 | # If true, an OpenSearch description file will be output, and all pages will 203 | # contain a tag referring to it. The value of this option must be the 204 | # base URL from which the finished HTML is served. 205 | #html_use_opensearch = '' 206 | 207 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 208 | #html_file_suffix = None 209 | 210 | # Language to be used for generating the HTML full-text search index. 211 | # Sphinx supports the following languages: 212 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 213 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 214 | #html_search_language = 'en' 215 | 216 | # A dictionary with options for the search language support, empty by default. 217 | # Now only 'ja' uses this config value 218 | #html_search_options = {'type': 'default'} 219 | 220 | # The name of a javascript file (relative to the configuration directory) that 221 | # implements a search results scorer. If empty, the default will be used. 222 | #html_search_scorer = 'scorer.js' 223 | 224 | # Output file base name for HTML help builder. 225 | htmlhelp_basename = 'Flask-AppFactorydoc' 226 | 227 | # -- Options for LaTeX output --------------------------------------------- 228 | 229 | latex_elements = { 230 | # The paper size ('letterpaper' or 'a4paper'). 231 | #'papersize': 'letterpaper', 232 | 233 | # The font size ('10pt', '11pt' or '12pt'). 234 | #'pointsize': '10pt', 235 | 236 | # Additional stuff for the LaTeX preamble. 237 | #'preamble': '', 238 | 239 | # Latex figure (float) alignment 240 | #'figure_align': 'htbp', 241 | } 242 | 243 | # Grouping the document tree into LaTeX files. List of tuples 244 | # (source start file, target name, title, 245 | # author, documentclass [howto, manual, or own class]). 246 | latex_documents = [ 247 | (master_doc, 'Flask-AppFactory.tex', u'Flask-AppFactory Documentation', 248 | u'Invenio Software', 'manual'), 249 | ] 250 | 251 | # The name of an image file (relative to this directory) to place at the top of 252 | # the title page. 253 | #latex_logo = None 254 | 255 | # For "manual" documents, if this is true, then toplevel headings are parts, 256 | # not chapters. 257 | #latex_use_parts = False 258 | 259 | # If true, show page references after internal links. 260 | #latex_show_pagerefs = False 261 | 262 | # If true, show URL addresses after external links. 263 | #latex_show_urls = False 264 | 265 | # Documents to append as an appendix to all manuals. 266 | #latex_appendices = [] 267 | 268 | # If false, no module index is generated. 269 | #latex_domain_indices = True 270 | 271 | 272 | # -- Options for manual page output --------------------------------------- 273 | 274 | # One entry per manual page. List of tuples 275 | # (source start file, name, description, authors, manual section). 276 | man_pages = [ 277 | (master_doc, 'Flask-AppFactory', u'Flask-AppFactory Documentation', 278 | [author], 1) 279 | ] 280 | 281 | # If true, show URL addresses after external links. 282 | #man_show_urls = False 283 | 284 | 285 | # -- Options for Texinfo output ------------------------------------------- 286 | 287 | # Grouping the document tree into Texinfo files. List of tuples 288 | # (source start file, target name, title, author, 289 | # dir menu entry, description, category) 290 | texinfo_documents = [ 291 | (master_doc, 'Flask-AppFactory', u'Flask-AppFactory Documentation', 292 | author, 'Flask-AppFactory', 'One line description of project.', 293 | 'Miscellaneous'), 294 | ] 295 | 296 | # Documents to append as an appendix to all manuals. 297 | #texinfo_appendices = [] 298 | 299 | # If false, no module index is generated. 300 | #texinfo_domain_indices = True 301 | 302 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 303 | #texinfo_show_urls = 'footnote' 304 | 305 | # If true, do not generate a @detailmenu in the "Top" node's menu. 306 | #texinfo_no_detailmenu = False 307 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CHANGES 2 | 3 | .. include:: ../CONTRIBUTING.rst 4 | 5 | License 6 | ======= 7 | 8 | .. include:: ../LICENSE 9 | 10 | .. include:: ../AUTHORS 11 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Flask-AppFactory 3 | ================== 4 | .. currentmodule:: flask_appfactory 5 | 6 | .. raw:: html 7 | 8 |

9 | 10 | travis-ci badge 12 | 13 | 14 | coveralls.io badge 16 | 17 |

18 | 19 | .. automodule:: flask_appfactory 20 | 21 | User's Guide 22 | ============ 23 | 24 | This part of the documentation will show you how to get started in using 25 | Flask-AppFactory with Flask. 26 | 27 | .. toctree:: 28 | :maxdepth: 2 29 | 30 | installation 31 | tutorial 32 | 33 | 34 | API Reference 35 | ============= 36 | 37 | If you are looking for information on a specific function, class or 38 | method, this part of the documentation is for you. 39 | 40 | .. toctree:: 41 | :maxdepth: 2 42 | 43 | api 44 | 45 | 46 | Additional Notes 47 | ================ 48 | 49 | Notes on how to contribute, legal information and changes are here for the interested. 50 | 51 | .. toctree:: 52 | :maxdepth: 2 53 | 54 | contributing 55 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ============ 5 | 6 | Install Flask-AppFactory with ``pip`` :: 7 | 8 | pip install flask-appfactory 9 | 10 | The development version can be downloaded from `its page at GitHub 11 | `_. :: 12 | 13 | git clone https://github.com/inveniosoftware/flask-appfactory.git 14 | cd flask-appfactory 15 | python setup.py develop 16 | ./run-tests.sh 17 | 18 | **Requirements** 19 | 20 | Flask-AppFactory has the following dependencies: 21 | 22 | * `Flask `_ 23 | * `Flask-Registry `_ 24 | * `Flask-CLI `_ 25 | 26 | Flask-AppFactory requires Python version 2.7 or 3.3+ 27 | -------------------------------------------------------------------------------- /docs/tut_app.rst: -------------------------------------------------------------------------------- 1 | Step 3: Creating an application 2 | =============================== 3 | 4 | You now have a reusable package and an extension. The last step is to make 5 | it into a fully functioning Flask application, and the step where 6 | Flask-AppFactory will do all the heavy lifting for you. 7 | 8 | Here's how the directory structure looks like:: 9 | 10 | myapp/__init__.py (empty) 11 | myapp/app.py 12 | myapp/cli.py 13 | myapp/config.py 14 | myapp/wsgi.py 15 | setup.py 16 | 17 | 18 | Application configuration 19 | ------------------------- 20 | First we define how our application is assembled by providing the default 21 | application configuration: 22 | 23 | .. literalinclude:: ../examples/myapp/config.py 24 | :language: python 25 | :linenos: 26 | 27 | The ``PACKAGES`` defines the list of resuable packages that 28 | Flask-AppFactory should load. The order of packages is important as templates, 29 | translations etc. is loaded according to this order. 30 | 31 | The ``EXTENSIONS`` defines the list of extensions that Flask-AppFactory 32 | should load. You'll see that in addition to ``myexts.sqlalchemy`` we also 33 | load ``flask_appfactory.ext.jinja2``. This extension is needed in order to make 34 | the template loader aware of the order of packages. We also load 35 | ``flask_celeryext:FlaskCeleryExt``, which will be explained in detail in 36 | Step 4 together with ``BROKER_URL``. 37 | 38 | 39 | Application factory 40 | ------------------- 41 | Next, we create our Flask `application factory `_, 42 | by using the ``flask_appfactory.appfactory()``. The ``appfactory`` 43 | method is passed: 44 | 45 | - the name of the Flask application (line 7). 46 | - the Python import path of our application configuration (line 8). 47 | - whether to load just configuration or the entire application (line 9). 48 | - and optional keyword arguments which will be merged with the configuration 49 | (line 10). 50 | 51 | .. literalinclude:: ../examples/myapp/app.py 52 | :language: python 53 | :linenos: 54 | 55 | The application factory will take care of loading all extensions and reusable 56 | packages. In addition to the configuration provided in line 8, the factory 57 | will try to load configuration from: 58 | 59 | 1. Instance folder (``/.cfg``). 60 | 2. The keyword arguments (``**kwargs_config``). 61 | 3. Environment variables. 62 | 63 | This allows you to have configuration for test/production environments or 64 | ingesting configuration into the Flask application in e.g. docker containers. 65 | 66 | Command Line Interface factory 67 | ------------------------------ 68 | Next, we create the CLI for our application that we can use to manage the 69 | application and run e.g. the development server: 70 | 71 | .. literalinclude:: ../examples/myapp/cli.py 72 | :language: python 73 | :linenos: 74 | 75 | We simply import our application factory (line 5) and pass it to the 76 | ``flask_appfactory.clifactory()`` method (line 7). 77 | 78 | The actual management script we install with an entry point in the package's 79 | ``setup.py`` (line 13): 80 | 81 | .. literalinclude:: ../examples/setup.py 82 | :language: python 83 | :linenos: 84 | :emphasize-lines: 11-15 85 | 86 | The new management script will besides your applications commands also have two 87 | commands to 1) run a development server and 2) start a interactive Python shell 88 | with the Flask application context. 89 | 90 | .. code-block:: console 91 | 92 | $ myapp 93 | Usage: myapp [OPTIONS] COMMAND [ARGS]... 94 | 95 | Options: 96 | --help Show this message and exit. 97 | 98 | Commands: 99 | initdb Initialize database. 100 | run Runs a development server. 101 | shell Runs a shell in the app context. 102 | testapp Command with application context. 103 | testsimple Command without application context. 104 | 105 | The command ``initdb`` was provided by our extension, while ``testapp`` and 106 | ``testsimple`` is provided by our reusable package. 107 | 108 | .. note:: 109 | It is also possible to use the ``flask`` command instead of creating a 110 | custom script. This can be achieved by creating a file ``manage.py``: 111 | 112 | .. literalinclude:: ../examples/manage.py 113 | :language: python 114 | :linenos: 115 | 116 | Next, export ``FLASK_APP`` environment variable and point it to your 117 | manage.py file: 118 | 119 | .. code-block:: console 120 | 121 | $ export FLASK_APP=/path/to/manage.py 122 | $ flask initdb 123 | 124 | WSGI Application 125 | ---------------- 126 | 127 | Last but not least, you will likely need a WSGI file to run your service in 128 | production environment: 129 | 130 | .. literalinclude:: ../examples/myapp/wsgi.py 131 | :language: python 132 | :linenos: 133 | -------------------------------------------------------------------------------- /docs/tut_celery.rst: -------------------------------------------------------------------------------- 1 | Step 4: Celery based background tasks 2 | ===================================== 3 | 4 | Flask-AppFactory includes optional support for Celery integration via the 5 | `Flask-CeleryExt `_ extension. If you 6 | wish to use it, be sure to install Flask-AppFactory like this:: 7 | 8 | pip install Flask-AppFactory[celery] 9 | 10 | To enable Celery support we add one file to our application package, and one 11 | file to our reusable package:: 12 | 13 | myapp/celery.py 14 | mymodule/tasks.py 15 | 16 | 17 | Configuration 18 | ------------- 19 | In our configuration we first have to add the Flask-CeleryExt extension 20 | (line 5) as well as define the Celery broker via the ``BROKER_URL`` variable 21 | (line 13). Note in this example we use a local Redis installation. For other 22 | options see the Celery documentation. 23 | 24 | .. literalinclude:: ../examples/myapp/config.py 25 | :language: python 26 | :linenos: 27 | :emphasize-lines: 5, 13 28 | 29 | The Flask-CeleryExt takes care of creating a minimal Celery application with 30 | the correct configuration so Celery knows e.g. which broker to use. In addition 31 | the minimal Celery application doesn't load any tasks to ensure faster startup 32 | time. 33 | 34 | See the `Celery documentation 35 | `_ for all the 36 | possible configuration variables. 37 | 38 | Celery application 39 | ------------------ 40 | Next, we create a small Celery application which is later used to start the 41 | Celery worker from. 42 | 43 | .. literalinclude:: ../examples/myapp/celery.py 44 | :language: python 45 | :linenos: 46 | 47 | Reusable tasks 48 | -------------- 49 | The reusable packages can easily define Celery tasks by adding a ``tasks.py`` 50 | file like this: 51 | 52 | .. literalinclude:: ../examples/mymodule/tasks.py 53 | :language: python 54 | :linenos: 55 | :emphasize-lines: 8, 13 56 | 57 | 58 | Notice the use of ``@shared_task`` decorator (line 8). This ensures that the 59 | task can be reused by many different Celery applications. The Celery 60 | application created above takes care of register the tasks. 61 | 62 | Each task is executed within a Flask application context (notice the use of 63 | e.g. ``current_app``). If you need to have a task executed in a request context 64 | (e.g. if you need to ensure before first request functions have been run), you 65 | just have to change the base class to use the ``RequestContextTask`` (line 13). 66 | 67 | Running the worker 68 | ------------------ 69 | 70 | Next, you can start a Celery worker by simply pointing Celery to your new 71 | Celery application in ``myapp.celery``: 72 | 73 | .. code-block:: console 74 | 75 | $ celery worker -A myapp.celery 76 | 77 | 78 | Sending tasks 79 | ------------- 80 | 81 | Executing tasks requires you to have your Flask application initialized, hence 82 | simply start a Python shell using your management script: 83 | 84 | .. code-block:: console 85 | 86 | $ myapp shell 87 | ... 88 | >>> from mymodule.tasks import appctx 89 | >>> appctx.delay() 90 | -------------------------------------------------------------------------------- /docs/tut_ext.rst: -------------------------------------------------------------------------------- 1 | Step 2: Enabling an extension 2 | ----------------------------- 3 | 4 | In step 1 you have created your first reusable package. Usually however, your 5 | packages will need access to e.g. a database backend, a cache etc. These 6 | features are enabled via extensions. 7 | 8 | Flask-SQLAlchemy extension 9 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | Next up in our tutorial, we will enable the 11 | `Flask-SQLAlchemy `_ 12 | extension to provide database models for our application and reusable packages. 13 | 14 | Here's how the directory structure looks like:: 15 | 16 | myext/__init__.py (empty) 17 | myext/sqlalchemy.py 18 | 19 | Flask-AppFactory enables extensions simply by calling a method 20 | ``setup_app(app)`` with the Flask application object (line 16). This 21 | pattern allows each extension to customize the Flask application as they see 22 | fit. 23 | 24 | .. literalinclude:: ../examples/myexts/sqlalchemy.py 25 | :language: python 26 | :linenos: 27 | :emphasize-lines: 16, 18, 10 28 | 29 | Each extension can set default configuration (line 18). The extension should 30 | however not overwrite an existing value, hence ``app.config.setdefault`` 31 | is used. This is because any instance specific configuration has already been 32 | loaded at this point. 33 | 34 | By default Flask-AppFactory only loads configuration, blueprints and CLI from 35 | each reusable package. Each enabled extension can however load further modules 36 | from each package. E.g. in the Flask-SQLAlchemy case, we may want to load 37 | database models from a ``models.py`` in all enabled reusable packages. This is 38 | achieved by creating a registry (line 10). 39 | 40 | Each extension can natually also provide CLI commands (see line 26-30). 41 | 42 | 43 | Using Flask-SQLAlchemy in packages 44 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 | 46 | Now that the extension has been enabled, you'll want to use it in your 47 | packages. E.g. you could add a ``models.py`` file to the reusable package 48 | you created in step 1: 49 | 50 | .. literalinclude:: ../examples/mymodule/models.py 51 | :language: python 52 | :linenos: 53 | 54 | -------------------------------------------------------------------------------- /docs/tut_package.rst: -------------------------------------------------------------------------------- 1 | Step 1: Creating a reusable package 2 | ----------------------------------- 3 | First, let's start by creating a small reusable package that we would possibly 4 | like to use in many different applications. The package consists of: 5 | 6 | - a Flask blueprint + templates. 7 | - a Click command line interface. 8 | - default configuration for the package. 9 | 10 | Here's how the directory structure looks like:: 11 | 12 | mymodule/__init__.py (empty) 13 | mymodule/cli.py 14 | mymodule/config.py 15 | mymodule/views.py 16 | mymodule/templates/mymodule.html 17 | mymodule/templates/mymodule_base.html 18 | 19 | 20 | Default configuration 21 | ~~~~~~~~~~~~~~~~~~~~~ 22 | The package can provide default configuration values in a ``config.py`` that it 23 | expects to be set. The values are merged into the Flask applications 24 | configuration and can be overwritten by each instance of an application. 25 | 26 | .. literalinclude:: ../examples/mymodule/config.py 27 | :language: python 28 | :linenos: 29 | 30 | Blueprint 31 | ~~~~~~~~~ 32 | The package can also provide a blueprint. The blueprint will automatically be 33 | registered on the Flask application by Flask-AppFactory. 34 | 35 | 36 | .. literalinclude:: ../examples/mymodule/views.py 37 | :language: python 38 | :linenos: 39 | :emphasize-lines: 8, 15 40 | 41 | This example blueprint simply renders the template ``mymodule.html`` and 42 | pass it the value of ``MYMODULE_GREETING`` (line 15) that we ensured was set in 43 | ``config.py``. Notice, also that the Blueprint's template folder is set in line 44 | 8 (without it, the templates in the next section are not found). 45 | 46 | 47 | Templates 48 | ~~~~~~~~~ 49 | The package provides two templates ``mymodule.html`` and ``mymodule_base.html`` 50 | , where ``mymodule.html`` simply extends ``mymodule_base.html``. The reason for 51 | this slightly odd method, is that it allows other packages to easily modify the 52 | templates without copy/pasting the entire template code. Another package simply 53 | creates a ``mymodule.html`` also extending from ``mymodule_base.html``, and 54 | only overwrites the few template blocks that it needs to customize. 55 | 56 | .. literalinclude:: ../examples/mymodule/templates/mymodule.html 57 | :language: html 58 | :linenos: 59 | 60 | .. literalinclude:: ../examples/mymodule/templates/mymodule_base.html 61 | :language: html 62 | :linenos: 63 | 64 | .. note:: 65 | It is usually a good idea to put your templates into a subfolder to avoid 66 | name conflicts between multiple packages. 67 | 68 | 69 | Command Line interface 70 | ~~~~~~~~~~~~~~~~~~~~~~ 71 | 72 | Finally, our package provide some simple CLI commands in ``cli.py`` that will 73 | be merged into a single CLI application that can used to manage a Flask 74 | application. 75 | 76 | The CLI is based on the `Click `_ Python package which 77 | has good extensive documentation. 78 | 79 | .. literalinclude:: ../examples/mymodule/cli.py 80 | :language: python 81 | :linenos: 82 | :emphasize-lines: 18, 13 83 | 84 | Flask-AppFactory expects to find a variable ``commands`` in ``cli.py`` 85 | with a list of commands to register (line 18). Using the 86 | ``@with_appcontext`` (line 13) decorator the commands can access the 87 | Flask application context (e.g ``current_app``). Without the decorator 88 | the application is not fully loaded in order to speed up the CLI. 89 | -------------------------------------------------------------------------------- /docs/tutorial.rst: -------------------------------------------------------------------------------- 1 | Tutorial 2 | ======== 3 | 4 | 5 | You want to develop applications and modules based on Flask-AppFactory? Here 6 | is a simple example walk through, to get an idea how it looks like. 7 | 8 | The full source code by viewed in the ``examples`` folder in the package or on 9 | `GitHub `_. 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | 14 | tut_package 15 | tut_ext 16 | tut_app 17 | tut_celery 18 | -------------------------------------------------------------------------------- /examples/manage.py: -------------------------------------------------------------------------------- 1 | # manage.py 2 | from myapp.app import create_app 3 | from flask_appfactory import load_cli 4 | app = create_app() 5 | load_cli(app) 6 | -------------------------------------------------------------------------------- /examples/myapp/__init__.py: -------------------------------------------------------------------------------- 1 | """Example Flask application using Flask-AppFactory.""" 2 | -------------------------------------------------------------------------------- /examples/myapp/app.py: -------------------------------------------------------------------------------- 1 | # myapp/app.py 2 | 3 | from flask_appfactory import appfactory 4 | 5 | def create_app(load=True, **kwargs_config): 6 | return appfactory( 7 | "myapp", 8 | "myapp.config", 9 | load=load, 10 | **kwargs_config 11 | ) 12 | -------------------------------------------------------------------------------- /examples/myapp/celery.py: -------------------------------------------------------------------------------- 1 | # myapp/celery.py 2 | 3 | from flask_appfactory.celery import celeryfactory 4 | from .app import create_app 5 | 6 | celery = celeryfactory(create_app()) 7 | -------------------------------------------------------------------------------- /examples/myapp/cli.py: -------------------------------------------------------------------------------- 1 | # myapp/cli.py 2 | 3 | from __future__ import absolute_import 4 | from flask_appfactory.cli import clifactory 5 | from .app import create_app 6 | 7 | cli = clifactory(create_app) 8 | -------------------------------------------------------------------------------- /examples/myapp/config.py: -------------------------------------------------------------------------------- 1 | # myapp/config.py 2 | 3 | EXTENSIONS = [ 4 | "flask_appfactory.ext.jinja2", 5 | "flask_celeryext:FlaskCeleryExt", 6 | "myexts.sqlalchemy", 7 | ] 8 | 9 | PACKAGES = [ 10 | "mymodule", 11 | ] 12 | 13 | BROKER_URL = "redis://localhost:6379/0" 14 | -------------------------------------------------------------------------------- /examples/myapp/wsgi.py: -------------------------------------------------------------------------------- 1 | # myapp/wsgi.py 2 | 3 | from __future__ import absolute_import 4 | from .app import create_app 5 | 6 | application = create_app() 7 | -------------------------------------------------------------------------------- /examples/myexts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inveniosoftware-attic/flask-appfactory/c746e9436418f3f4cb03f1c511af19d62145b7a3/examples/myexts/__init__.py -------------------------------------------------------------------------------- /examples/myexts/sqlalchemy.py: -------------------------------------------------------------------------------- 1 | # myexts/sqlalchemy.py 2 | 3 | import click 4 | from flask_cli import with_appcontext 5 | from flask.ext.sqlalchemy import SQLAlchemy 6 | from flask_registry import ModuleAutoDiscoveryRegistry, RegistryProxy 7 | 8 | db = SQLAlchemy() 9 | 10 | models = RegistryProxy( 11 | 'models', # Registry namespace 12 | ModuleAutoDiscoveryRegistry, 13 | 'models' # Module name (i.e. models.py) 14 | ) 15 | 16 | def setup_app(app): 17 | # Set default configuration 18 | app.config.setdefault( 19 | 'SQLALCHEMY_DATABASE_URI', 20 | 'sqlite:////tmp/test.db' 21 | ) 22 | # Add extension CLI to application. 23 | app.cli.add_command(initdb) 24 | db.init_app(app) 25 | 26 | @click.command() 27 | @with_appcontext 28 | def initdb(): 29 | """Initialize database.""" 30 | db.create_all() 31 | -------------------------------------------------------------------------------- /examples/mymodule/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inveniosoftware-attic/flask-appfactory/c746e9436418f3f4cb03f1c511af19d62145b7a3/examples/mymodule/__init__.py -------------------------------------------------------------------------------- /examples/mymodule/cli.py: -------------------------------------------------------------------------------- 1 | # mymodule/cli.py 2 | 3 | import click 4 | from flask import current_app 5 | from flask_cli import with_appcontext 6 | 7 | @click.command() 8 | def testsimple(): 9 | """Command without application context.""" 10 | click.echo("Test") 11 | 12 | @click.command() 13 | @with_appcontext 14 | def testapp(): 15 | """Command with application context.""" 16 | click.echo(current_app.name) 17 | 18 | commands = [testsimple, testapp] 19 | -------------------------------------------------------------------------------- /examples/mymodule/config.py: -------------------------------------------------------------------------------- 1 | # mymodule/config.py 2 | 3 | MYMODULE_GREETING = "Hello, World!" 4 | -------------------------------------------------------------------------------- /examples/mymodule/models.py: -------------------------------------------------------------------------------- 1 | # mymodule/models.py 2 | 3 | from myexts.sqlalchemy import db 4 | 5 | class User(db.Model): 6 | id = db.Column(db.Integer, primary_key=True) 7 | username = db.Column(db.String(80), unique=True) 8 | email = db.Column(db.String(120), unique=True) 9 | -------------------------------------------------------------------------------- /examples/mymodule/tasks.py: -------------------------------------------------------------------------------- 1 | # mymodule/tasks.py 2 | 3 | from celery import shared_task 4 | from flask import current_app, request 5 | from flask_celeryext import RequestContextTask 6 | 7 | 8 | @shared_task 9 | def appctx(): 10 | current_app.logger.info(current_app.config['BROKER_URL']) 11 | 12 | 13 | @shared_task(base=RequestContextTask) 14 | def reqctx(): 15 | current_app.logger.info(request.method) 16 | -------------------------------------------------------------------------------- /examples/mymodule/templates/mymodule.html: -------------------------------------------------------------------------------- 1 | {# mymodule/templates/mymodule.html #} 2 | {% extends "mymodule_base.html" %} 3 | -------------------------------------------------------------------------------- /examples/mymodule/templates/mymodule_base.html: -------------------------------------------------------------------------------- 1 | {# mymodule/templates/mymodule_base.html #} 2 | {% block body %}{{greeting}}{% endblock %} 3 | -------------------------------------------------------------------------------- /examples/mymodule/views.py: -------------------------------------------------------------------------------- 1 | # mymodule/views.py 2 | 3 | from flask import Blueprint, current_app, render_template 4 | 5 | blueprint = Blueprint( 6 | 'mymodule', 7 | __name__, 8 | template_folder='templates', 9 | ) 10 | 11 | @blueprint.route("/") 12 | def index(): 13 | return render_template( 14 | 'mymodule.html', 15 | greeting=current_app.config['MYMODULE_GREETING'], 16 | ) 17 | -------------------------------------------------------------------------------- /examples/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | 3 | from setuptools import setup 4 | 5 | setup( 6 | name='MyApp', 7 | version="1.0", 8 | packages=['myapp', "myexts", "mymodule"], 9 | zip_safe=False, 10 | include_package_data=True, 11 | entry_points={ 12 | 'console_scripts': [ 13 | 'myapp = myapp.cli:cli', 14 | ] 15 | }, 16 | install_requires=[ 17 | 'Flask>=0.10', 18 | 'Flask-AppFactory[celery]', 19 | 'Flask-SQLAlchemy', 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /flask_appfactory/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Flask-AppFactory is an dynamic application loader. 11 | 12 | It allows you to build reusable modules that can be easily be assembled into 13 | full Flask applications using this loader. Each reusable module can provide 14 | default configuration, blueprints and command line interface. 15 | """ 16 | 17 | from __future__ import absolute_import, unicode_literals, print_function 18 | 19 | from .app import appfactory 20 | from .cli import clifactory, load_cli 21 | 22 | from .version import __version__ 23 | 24 | __all__ = ('appfactory', 'clifactory', 'load_cli', '__version__') 25 | -------------------------------------------------------------------------------- /flask_appfactory/app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Flask application factory.""" 11 | 12 | from __future__ import absolute_import, print_function, unicode_literals 13 | 14 | import logging 15 | import os 16 | import re 17 | import sys 18 | import warnings 19 | 20 | import ast 21 | from flask import Flask 22 | from flask_cli import FlaskCLI 23 | from flask_registry import BlueprintAutoDiscoveryRegistry, \ 24 | ConfigurationRegistry, ExtensionRegistry, PackageRegistry, Registry 25 | 26 | 27 | def configure_warnings(): 28 | """Configure warnings by routing warnings to the logging system. 29 | 30 | It also unhides ``DeprecationWarning``. 31 | """ 32 | if not sys.warnoptions: 33 | # Route warnings through python logging 34 | logging.captureWarnings(True) 35 | 36 | # DeprecationWarning is by default hidden, hence we force the 37 | # "default" behavior on deprecation warnings which is not to hide 38 | # errors. 39 | warnings.simplefilter("default", DeprecationWarning) 40 | 41 | 42 | def load_config(app, module_name, **kwargs_config): 43 | """Load configuration. 44 | 45 | Configuration is loaded in the following order: 46 | 47 | 1. Configuration module (i.e. ``module_name``). 48 | 2. Instance configuration in ``/.cfg`` 49 | 3. Keyword configuration arguments. 50 | 4. Environment variables specified in ``_APP_CONFIG_ENVS`` 51 | configuration variable or comma separated list in environment variable 52 | with the same name. 53 | 54 | Additionally checks if ``SECRET_KEY`` is set in the configuration and warns 55 | if it is not. 56 | 57 | :param app: Flask application. 58 | :param module_name: Configuration module. 59 | :param kwargs_config: Configuration keyword arguments 60 | """ 61 | # 1. Load site specific default configuration 62 | if module_name: 63 | app.config.from_object(module_name) 64 | 65 | # 2. Load .cfg from instance folder 66 | app.config.from_pyfile('{0}.cfg'.format(app.name), silent=True) 67 | 68 | # 3. Update application config from parameters. 69 | app.config.update(kwargs_config) 70 | 71 | # 4. Update config with specified environment variables. 72 | envvars = '{0}_APP_CONFIG_ENVS'.format(app.name.upper()) 73 | 74 | for cfg_name in app.config.get(envvars, os.getenv(envvars, '')).split(','): 75 | cfg_name = cfg_name.strip().upper() 76 | if cfg_name: 77 | cfg_value = app.config.get(cfg_name) 78 | cfg_value = os.getenv(cfg_name, cfg_value) 79 | try: 80 | cfg_value = ast.literal_eval(cfg_value) 81 | except (SyntaxError, ValueError): 82 | pass 83 | app.config[cfg_name] = cfg_value 84 | app.logger.debug("{0} = {1}".format(cfg_name, cfg_value)) 85 | 86 | # Ensure SECRET_KEY is set. 87 | SECRET_KEY = app.config.get('SECRET_KEY') 88 | 89 | if SECRET_KEY is None: 90 | app.config["SECRET_KEY"] = 'change_me' 91 | warnings.warn( 92 | "Set variable SECRET_KEY with random string in {}".format( 93 | os.path.join(app.instance_path, "{}.cfg".format(app.name)), 94 | ), UserWarning) 95 | 96 | # Initialize application registry, used for discovery and loading of 97 | # configuration, extensions and blueprints 98 | Registry(app=app) 99 | 100 | app.extensions['registry'].update( 101 | # Register packages listed in PACKAGES conf variable. 102 | packages=PackageRegistry(app)) 103 | 104 | app.extensions['loaded'] = False 105 | 106 | 107 | def load_application(app): 108 | """Load the application. 109 | 110 | Assembles the application by use of ``PACKAGES`` and ``EXTENSIONS`` 111 | configuration variables. 112 | 113 | 1. Load extensions by calling ``setup_app()`` in module defined in 114 | ``EXTENSIONS``. 115 | 2. Register blueprints from each module defined in ``PACAKGES`` by looking 116 | searching in ``views.py`` for a ``blueprint`` or ``blueprints`` 117 | variable. 118 | 119 | :param app: Flask application. 120 | """ 121 | # Extend application config with default configuration values from packages 122 | # (app config takes precedence) 123 | app.extensions['registry'].update( 124 | # Register extensions listed in EXTENSIONS conf variable. 125 | extensions=ExtensionRegistry(app), 126 | # Register blueprints from packages in PACKAGES configuration variable. 127 | blueprints=BlueprintAutoDiscoveryRegistry(app=app), 128 | ) 129 | 130 | ConfigurationRegistry(app) 131 | 132 | app.extensions['loaded'] = True 133 | 134 | 135 | def base_app(app_name, instance_path=None, static_folder=None, 136 | static_url_path='/static/', instance_relative_config=True, 137 | template_folder='templates', flask_cls=Flask): 138 | """Create a base Flask Application. 139 | 140 | Ensures instance path and is set and created. Instance path defaults to 141 | ``/var/-instance``. 142 | 143 | Additionally configure warnings to be routed to the Python logging system, 144 | and by default makes ``DeprecationWarning`` loud. 145 | 146 | .. versionchanged:: v0.2.0 147 | Added ``flask_cls`` parameter. 148 | 149 | :param app_name: Flask application name. 150 | :param instance_path: Instance path 151 | :param static_folder: Static folder. 152 | :param static_url_path: URL path of static folder. Default: ``/static/``. 153 | :param instance_relative_config: Use instance relative config 154 | Default: ``True``. 155 | :param template_folder: Template folder. Default: ``templates``. 156 | :param flask_cls: Flask Application class. Default: ``Flask``. 157 | """ 158 | configure_warnings() 159 | 160 | # Prefix for env variables 161 | env_prefix = re.sub('[^A-Z]', '', app_name.upper()) 162 | 163 | # Detect instance path 164 | instance_path = instance_path or \ 165 | os.getenv(env_prefix + '_INSTANCE_PATH') or \ 166 | os.path.join(sys.prefix, 'var', app_name + '-instance') 167 | 168 | # Detect static files path 169 | static_folder = static_folder or \ 170 | os.getenv(env_prefix + '_STATIC_FOLDER') or \ 171 | os.path.join(instance_path, 'static') 172 | 173 | # Create instance path 174 | try: 175 | if not os.path.exists(instance_path): 176 | os.makedirs(instance_path) 177 | except Exception: # pragma: no cover 178 | pass 179 | 180 | # Create the Flask application instance 181 | app = flask_cls( 182 | app_name, 183 | static_url_path=static_url_path, 184 | static_folder=static_folder, 185 | instance_relative_config=instance_relative_config, 186 | instance_path=instance_path, 187 | template_folder=template_folder, 188 | ) 189 | 190 | # Compatibility layer to support Flask 1.0 click integration on v0.10 191 | FlaskCLI(app=app) 192 | 193 | return app 194 | 195 | 196 | def appfactory(app_name, module_name, load=True, **kwargs_config): 197 | """Create a Flask application according to a defined configuration. 198 | 199 | :param app_name: Flask application name. 200 | :param module_name: Python configuration module. 201 | :param load: Load application (instead of only the configuration). 202 | Default: ``True``. 203 | :param kwargs_config: Extra configuration variables for the Flask 204 | application. 205 | """ 206 | app = base_app(app_name) 207 | 208 | load_config(app, module_name, **kwargs_config) 209 | 210 | if load: 211 | load_application(app) 212 | 213 | return app 214 | -------------------------------------------------------------------------------- /flask_appfactory/celery.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Celery application factory.""" 11 | 12 | from __future__ import absolute_import, print_function, unicode_literals 13 | 14 | from flask_celeryext import create_celery_app 15 | from flask_registry import ModuleAutoDiscoveryRegistry 16 | 17 | 18 | def load_tasks(app): 19 | """Load Celery tasks from installed packages. 20 | 21 | .. versionadded:: v0.2.0 22 | """ 23 | app.extensions['registry']['tasks'] = ModuleAutoDiscoveryRegistry( 24 | module_name='tasks', app=app) 25 | 26 | 27 | def celeryfactory(app): 28 | """Create a Celery application based on Flask application. 29 | 30 | .. versionadded:: v0.2.0 31 | 32 | :param app: Flask application instance. 33 | """ 34 | try: 35 | # Check if celery application has already been created. 36 | celery = app.extensions['flask-celeryext'].celery 37 | except KeyError: 38 | celery = create_celery_app(app) 39 | load_tasks(app) 40 | 41 | return celery 42 | -------------------------------------------------------------------------------- /flask_appfactory/cli.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Command line interface factory.""" 11 | 12 | from __future__ import absolute_import, print_function, unicode_literals 13 | 14 | import click 15 | from flask_cli import FlaskGroup 16 | from flask_registry import ModuleAutoDiscoveryRegistry 17 | 18 | from .app import load_application 19 | 20 | 21 | class CLIDiscoveryRegistry(ModuleAutoDiscoveryRegistry): 22 | 23 | """Discover CLI modules and register them on a command collection. 24 | 25 | Searches for a variable ``commands`` in a module ``cli`` in each package. 26 | The variable must be a list of commands/groups to register, e.g: 27 | 28 | .. code-block:: python 29 | 30 | import click 31 | 32 | @click.command() 33 | def testcmd(): 34 | click.echo("Test") 35 | 36 | commands = [testcmd, ] 37 | 38 | :param cli: A ``click.Command`` or ``click.Group`` object. 39 | :param app: Flask application. 40 | """ 41 | 42 | def __init__(self, cli, app, **kwargs): 43 | """Initialize the registry.""" 44 | self.cli = cli 45 | super(CLIDiscoveryRegistry, self).__init__('cli', app=app, **kwargs) 46 | 47 | def register(self, module): 48 | """Register modules with CLI variable.""" 49 | module_commands = getattr(module, 'commands', None) 50 | if module_commands is not None: 51 | for c in module_commands: 52 | if isinstance(c, click.BaseCommand): 53 | self.cli.add_command(c) 54 | 55 | super(CLIDiscoveryRegistry, self).register(module) 56 | 57 | 58 | def load_cli(app, cli=None): 59 | """Load CLI commands and register them on CLI application. 60 | 61 | :param app: Flask application instance. 62 | :param cli: Click command group. If no group is provided, the commands are 63 | registered on the Flask applications cli. 64 | """ 65 | CLIDiscoveryRegistry(app.cli if cli is None else cli, app) 66 | 67 | 68 | def clifactory(create_app, **config): 69 | """Create a click CLI application based on configuration. 70 | 71 | The CLI will install the default ``run`` and ``shell`` commands from Flask, 72 | and load commands from the list of modules defined in ``PACKAGES``. It will 73 | search in ``cli.py`` in each module for a variable ``cli``. 74 | 75 | The Flask application is not fully loaded unless the Flask app context is 76 | required. 77 | 78 | :param create_app: Flask application factory function. 79 | """ 80 | # Create application object without loading the full application. 81 | app = create_app(load=False, **config) 82 | 83 | def create_cli_app(info): 84 | if not app.extensions['loaded']: 85 | load_application(app) 86 | return app 87 | 88 | @click.group(cls=FlaskGroup, create_app=create_cli_app) 89 | def cli(**params): 90 | pass 91 | 92 | # # Create command collection 93 | # cli = AppFactoryCollection(create_cli_app) 94 | # cli.add_source(flask_cli) 95 | 96 | # Register CLI modules from packages. 97 | load_cli(app, cli) 98 | 99 | return cli 100 | -------------------------------------------------------------------------------- /flask_appfactory/ext/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Extensions for Flask-AppFactory.""" 11 | 12 | from __future__ import absolute_import, unicode_literals, print_function 13 | -------------------------------------------------------------------------------- /flask_appfactory/ext/jinja2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Order-aware Jinja2 loader and extensions initialization. 11 | 12 | The default Flask Jinja2 loader is not aware of the order defined in 13 | ``PACKAGES``. This means that if two modules provides the same template, it is 14 | undefined which template is being rendered. This extension adds a 15 | ``PACKAGES`` order-aware Jinja2 loader, which will search for a given template 16 | in each module in the order defined by ``PACKAGES``. This allows modules to 17 | override templates defined in modules later in ``PACKAGES``. 18 | 19 | 20 | Additionally the extension will load any Jinja2 extension defined in the 21 | ``JINJA2_EXTENSIONS`` configuration variable. 22 | """ 23 | 24 | from __future__ import absolute_import, print_function, unicode_literals 25 | 26 | from distutils.version import LooseVersion 27 | from flask import __version__ as flask_version 28 | from flask.templating import DispatchingJinjaLoader 29 | from jinja2 import ChoiceLoader 30 | 31 | # Flask 1.0 changes return value of _iter_loaders so for compatibility with 32 | # both Flask 0.10 and 1.0 we here check the version. 33 | # See Flask commit bafc13981002dee4610234c7c97ac176766181c1 34 | IS_FLASK_1_0 = LooseVersion(flask_version) >= LooseVersion("0.11-dev") 35 | 36 | try: 37 | # Deprecated in Flask commit 817b72d484d353800d907b3580c899314bf7f3c6 38 | from flask.templating import blueprint_is_module 39 | except ImportError: # pragma: no cover 40 | def blueprint_is_module(blueprint): 41 | """Dummy function for Flask 1.0.""" 42 | return False 43 | 44 | 45 | class OrderAwareDispatchingJinjaLoader(DispatchingJinjaLoader): 46 | 47 | """Order aware dispatching Jinja loader. 48 | 49 | Customization of default Flask Jinja2 template loader. By default the 50 | Flask Jinja2 template loader is not aware of the order of Blueprints as 51 | defined by the ``PACKAGES`` configuration variable. 52 | """ 53 | 54 | def _iter_loaders(self, template): 55 | for blueprint in self.app.extensions['registry']['blueprints']: 56 | if blueprint_is_module(blueprint): 57 | continue # pragma: no cover 58 | loader = blueprint.jinja_loader 59 | if loader is not None: 60 | if IS_FLASK_1_0: 61 | yield blueprint, loader # pragma: no cover 62 | else: 63 | yield loader, template 64 | 65 | 66 | def setup_app(app): 67 | """Initialize Jinja2 loader and extensions.""" 68 | # Customize Jinja loader. 69 | jinja_loader = ChoiceLoader([ 70 | OrderAwareDispatchingJinjaLoader(app), 71 | app.jinja_loader, 72 | ]) 73 | app.jinja_loader = jinja_loader 74 | 75 | # Load Jinja extensions 76 | for ext in app.config.get('JINJA2_EXTENSIONS', []): 77 | app.jinja_env.add_extension(ext) 78 | -------------------------------------------------------------------------------- /flask_appfactory/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Version information for Flask-AppFactory. 11 | 12 | This file is imported by ``flask_appfactory.__init__``, and parsed by 13 | ``setup.py`` as well as ``docs/conf.py``. 14 | """ 15 | 16 | # Do not change the format of this next line. Doing so risks breaking 17 | # setup.py and docs/conf.py 18 | 19 | __version__ = "0.2.2.dev20150818" 20 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | # This file is part of Flask-AppFactory 2 | # Copyright (C) 2015 CERN. 3 | # 4 | # Flask-AppFactory is free software; you can redistribute it and/or 5 | # modify it under the terms of the Revised BSD License; see LICENSE 6 | # file for more details. 7 | 8 | [pytest] 9 | addopts = --clearcache --pep8 --ignore=docs --cov=flask_appfactory --cov-report=term-missing tests flask_appfactory 10 | -------------------------------------------------------------------------------- /requirements.devel.txt: -------------------------------------------------------------------------------- 1 | -e git+https://github.com/mitsuhiko/flask.git#egg=Flask 2 | -e git+https://github.com/inveniosoftware/flask-registry.git#egg=Flask-Registry 3 | -e git+https://github.com/inveniosoftware/flask-cli.git#egg=Flask-CLI 4 | -e git+https://github.com/inveniosoftware/flask-celeryext.git#egg=Flask-CeleryExt 5 | -------------------------------------------------------------------------------- /requirements.latest.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | click 3 | Flask-Registry 4 | Flask-CLI 5 | Flask-CeleryExt 6 | -------------------------------------------------------------------------------- /requirements.lowest.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10 2 | click==2.0 3 | Flask-Registry==0.2.0 4 | Flask-CLI==0.2.1 5 | Flask-CeleryExt==0.1.0 6 | -------------------------------------------------------------------------------- /run-tests.sh: -------------------------------------------------------------------------------- 1 | # This file is part of Flask-AppFactory 2 | # Copyright (C) 2015 CERN. 3 | # 4 | # Flask-AppFactory is free software; you can redistribute it and/or 5 | # modify it under the terms of the Revised BSD License; see LICENSE 6 | # file for more details. 7 | 8 | pep257 flask_appfactory && \ 9 | sphinx-build -qnNW docs docs/_build/html && \ 10 | python setup.py test && \ 11 | sphinx-build -qnNW -b doctest docs docs/_build/doctest 12 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # This file is part of Flask-AppFactory 2 | # Copyright (C) 2015 CERN. 3 | # 4 | # Flask-AppFactory is free software; you can redistribute it and/or 5 | # modify it under the terms of the Revised BSD License; see LICENSE 6 | # file for more details. 7 | 8 | [wheel] 9 | universal=1 10 | 11 | [build_sphinx] 12 | source-dir = docs/ 13 | build-dir = docs/_build 14 | all_files = 1 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Flask-AppFactory is an dynamic application loader.""" 11 | 12 | import os 13 | import re 14 | import sys 15 | 16 | from setuptools import setup 17 | from setuptools.command.test import test as TestCommand 18 | 19 | 20 | class PyTest(TestCommand): 21 | 22 | """Integration of PyTest with setuptools.""" 23 | 24 | user_options = [('pytest-args=', 'a', 'Arguments to pass to py.test')] 25 | 26 | def initialize_options(self): 27 | """Initialize options.""" 28 | TestCommand.initialize_options(self) 29 | try: 30 | from ConfigParser import ConfigParser 31 | except ImportError: 32 | from configparser import ConfigParser 33 | config = ConfigParser() 34 | config.read("pytest.ini") 35 | self.pytest_args = config.get("pytest", "addopts").split(" ") 36 | 37 | def finalize_options(self): 38 | """Finalize options.""" 39 | TestCommand.finalize_options(self) 40 | self.test_args = [] 41 | self.test_suite = True 42 | 43 | def run_tests(self): 44 | """Run tests.""" 45 | # import here, cause outside the eggs aren't loaded 46 | import pytest 47 | import _pytest.config 48 | pm = _pytest.config.get_plugin_manager() 49 | pm.consider_setuptools_entrypoints() 50 | errno = pytest.main(self.pytest_args) 51 | sys.exit(errno) 52 | 53 | # Get the version string. Cannot be done with import! 54 | with open(os.path.join('flask_appfactory', 'version.py'), 'rt') as f: 55 | version = re.search( 56 | '__version__\s*=\s*"(?P.*)"\n', 57 | f.read() 58 | ).group('version') 59 | 60 | tests_require = [ 61 | 'pytest-cache>=1.0', 62 | 'pytest-cov>=1.8.0', 63 | 'pytest-pep8>=1.0.6', 64 | 'pytest>=2.6.1', 65 | 'coverage<4.0a1', 66 | ] 67 | 68 | setup( 69 | name='Flask-AppFactory', 70 | version=version, 71 | description=__doc__, 72 | url='http://github.com/inveniosoftware/flask-appfactory/', 73 | license='BSD', 74 | author='Invenio Collaboration', 75 | author_email='info@inveniosoftware.org', 76 | long_description=open('README.rst').read(), 77 | packages=['flask_appfactory', 'flask_appfactory.ext', ], 78 | zip_safe=False, 79 | platforms='any', 80 | tests_require=tests_require, 81 | install_requires=[ 82 | 'Flask>=0.10', 83 | 'Flask-Registry>=0.2.0', 84 | 'Flask-CLI>=0.2.1', 85 | ], 86 | extras_require={ 87 | 'celery': ['Flask-CeleryExt>=0.1.0'], 88 | }, 89 | cmdclass={'test': PyTest}, 90 | classifiers=[ 91 | 'Programming Language :: Python :: 2', 92 | 'Programming Language :: Python :: 2.6', 93 | 'Programming Language :: Python :: 2.7', 94 | 'Programming Language :: Python :: 3', 95 | 'Programming Language :: Python :: 3.3', 96 | 'Programming Language :: Python :: 3.4', 97 | 'Environment :: Web Environment', 98 | 'Development Status :: 5 - Production/Stable', 99 | 'Intended Audience :: Developers', 100 | 'License :: OSI Approved :: BSD License', 101 | 'Operating System :: OS Independent', 102 | 'Topic :: Utilities', 103 | ], 104 | ) 105 | -------------------------------------------------------------------------------- /tests/simplemodule/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inveniosoftware-attic/flask-appfactory/c746e9436418f3f4cb03f1c511af19d62145b7a3/tests/simplemodule/__init__.py -------------------------------------------------------------------------------- /tests/simplemodule/cli.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | import click 11 | from flask import current_app 12 | from flask_cli import with_appcontext 13 | 14 | 15 | @click.command() 16 | def testsimple(): 17 | """.""" 18 | click.echo("Test Simple") 19 | 20 | 21 | @click.command() 22 | @with_appcontext 23 | def testapp(): 24 | """.""" 25 | click.echo("Test %s" % current_app.name) 26 | 27 | 28 | commands = [testsimple, testapp] 29 | -------------------------------------------------------------------------------- /tests/simplemodule/config.py: -------------------------------------------------------------------------------- 1 | SIMPLEMODULE_VAR = True 2 | -------------------------------------------------------------------------------- /tests/simplemodule/tasks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | from celery import shared_task 11 | 12 | 13 | @shared_task 14 | def simpletask(): 15 | """Simple test task.""" 16 | return "simpletask" 17 | -------------------------------------------------------------------------------- /tests/simplemodule/templates/test.html: -------------------------------------------------------------------------------- 1 | SIMPLEMODULE 2 | -------------------------------------------------------------------------------- /tests/simplemodule/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | from __future__ import absolute_import, unicode_literals, print_function 11 | 12 | from flask import Blueprint 13 | 14 | blueprint = Blueprint( 15 | 'simplemodule', 16 | __name__, 17 | template_folder='templates', 18 | static_folder='static' 19 | ) 20 | 21 | 22 | @blueprint.route("/") 23 | def index(): 24 | return 'TEST' 25 | 26 | 27 | @blueprint.route("/simplemodule") 28 | def template_test(): 29 | return render_template('test.html') 30 | -------------------------------------------------------------------------------- /tests/simplemodule2/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | -------------------------------------------------------------------------------- /tests/simplemodule2/templates/test.html: -------------------------------------------------------------------------------- 1 | SIMPLEMODULE2 2 | -------------------------------------------------------------------------------- /tests/simplemodule2/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | from __future__ import absolute_import, unicode_literals, print_function 11 | 12 | from flask import Blueprint, render_template 13 | 14 | blueprint = Blueprint( 15 | 'simplemodule2', 16 | __name__, 17 | template_folder='templates', 18 | static_folder='static' 19 | ) 20 | 21 | 22 | @blueprint.route("/") 23 | def index(): 24 | return 'TEST' 25 | 26 | 27 | @blueprint.route("/simplemodule2") 28 | def template_test(): 29 | return render_template('test.html') 30 | -------------------------------------------------------------------------------- /tests/test_app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Test app factory.""" 11 | 12 | from __future__ import absolute_import, print_function, unicode_literals 13 | 14 | import os 15 | import shutil 16 | import tempfile 17 | 18 | from flask_appfactory import appfactory 19 | from flask_appfactory.app import base_app 20 | 21 | 22 | def test_dummy_app(): 23 | """.""" 24 | class conf: 25 | SOMEVAR = True 26 | app = appfactory("dummyapp", conf) 27 | assert app.name == "dummyapp" 28 | assert app.config['SOMEVAR'] 29 | assert app.config['SECRET_KEY'] == "change_me" 30 | assert len(app.extensions['registry']['packages']) == 0 31 | assert len(app.extensions['registry']['blueprints']) == 0 32 | 33 | with app.test_client() as c: 34 | rv = c.get('/') 35 | assert rv.status_code == 404 36 | 37 | 38 | def test_conf_overwrite(): 39 | """.""" 40 | class conf: 41 | SOMEVAR = True 42 | app = appfactory("dummyapp", conf, SOMEVAR=False) 43 | assert app.config['SOMEVAR'] == False 44 | 45 | 46 | def test_envvars_overwrite(): 47 | """.""" 48 | class conf: 49 | SOMEVAR = True 50 | 51 | try: 52 | os.environ['MYAPP_APP_CONFIG_ENVS'] = 'SOMEVAR, SOMEVAR1' 53 | os.environ['SOMEVAR'] = 'False' 54 | os.environ['SOMEVAR1'] = '"V1"' 55 | os.environ['SOMEVAR2'] = '"V2"' 56 | 57 | app = appfactory("myapp", conf, SOMEVAR=True) 58 | # Env overrides kwargs 59 | assert app.config['SOMEVAR'] == False 60 | # Only vars specified in MYAPP_APP_CONFIG_ENVS is set. 61 | assert app.config['SOMEVAR1'] == "V1" 62 | assert 'SOMEVAR2' not in app.config 63 | finally: 64 | del os.environ['MYAPP_APP_CONFIG_ENVS'] 65 | del os.environ['SOMEVAR'] 66 | del os.environ['SOMEVAR1'] 67 | del os.environ['SOMEVAR2'] 68 | 69 | 70 | def test_envvars_string(): 71 | """.""" 72 | class conf: 73 | pass 74 | 75 | try: 76 | os.environ['MYAPP_APP_CONFIG_ENVS'] = 'SOMEVAR' 77 | os.environ['SOMEVAR'] = 'syntaxerror' 78 | app = appfactory("myapp", conf) 79 | assert app.config['SOMEVAR'] == "syntaxerror" 80 | finally: 81 | del os.environ['MYAPP_APP_CONFIG_ENVS'] 82 | del os.environ['SOMEVAR'] 83 | 84 | 85 | def test_simple_app(): 86 | """.""" 87 | class conf: 88 | PACKAGES = ['simplemodule'] 89 | app = appfactory("simpleapp", conf) 90 | 91 | assert app.extensions['loaded'] 92 | assert len(app.extensions['registry']['packages']) == 1 93 | assert len(app.extensions['registry']['blueprints']) == 1 94 | assert app.config['SIMPLEMODULE_VAR'] 95 | 96 | with app.test_client() as c: 97 | rv = c.get('/') 98 | assert rv.status_code == 200 99 | assert rv.data == 'TEST'.encode('utf-8') 100 | 101 | 102 | def test_instance_path_creation(): 103 | """Test instance path creation.""" 104 | path = tempfile.mkdtemp() 105 | try: 106 | instance_path = os.path.join(path, "myinstance") 107 | assert not os.path.exists(instance_path) 108 | base_app('instance', instance_path=instance_path) 109 | assert os.path.exists(instance_path) 110 | finally: 111 | shutil.rmtree(path) 112 | 113 | 114 | def test_simple_app_noload(): 115 | """.""" 116 | class conf: 117 | PACKAGES = ['simplemodule'] 118 | app = appfactory("simpleapp", conf, load=False) 119 | 120 | assert not app.extensions['loaded'] 121 | assert len(app.extensions['registry']['packages']) == 1 122 | assert 'blueprints' not in app.extensions['registry'] 123 | assert 'SIMPLEMODULE_VAR' not in app.config 124 | assert 'PACKAGES' in app.config 125 | 126 | with app.test_client() as c: 127 | assert c.get('/').status_code == 404 128 | -------------------------------------------------------------------------------- /tests/test_celery.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Test celery factory.""" 11 | 12 | from __future__ import absolute_import, print_function, unicode_literals 13 | 14 | from flask_appfactory import appfactory 15 | 16 | import pytest 17 | 18 | 19 | def test_dummy_app(): 20 | """"Test celery app creation.""" 21 | pytest.importorskip("celery") 22 | from flask_appfactory.celery import celeryfactory 23 | 24 | class conf: 25 | EXTENSIONS = ['flask_celeryext:FlaskCeleryExt'] 26 | 27 | app = appfactory("app2", conf) 28 | celery = celeryfactory(app) 29 | assert celery 30 | assert celery.flask_app == app 31 | assert app.extensions['flask-celeryext'].celery == celery 32 | 33 | 34 | def test_dummy_app_noext(): 35 | """"Test celery app creation without extension.""" 36 | pytest.importorskip("celery") 37 | from flask_appfactory.celery import celeryfactory 38 | app = appfactory("app3", None) 39 | celery = celeryfactory(app) 40 | assert celery 41 | assert celery.flask_app == app 42 | assert 'flask-celeryext' not in app.extensions 43 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Test app factory.""" 11 | 12 | from __future__ import absolute_import, print_function, unicode_literals 13 | 14 | from click.testing import CliRunner 15 | from flask import Flask 16 | from flask_appfactory import appfactory, clifactory 17 | from flask_cli import ScriptInfo 18 | 19 | 20 | def create_app(load=True): 21 | """Application factory used for testing.""" 22 | class conf: 23 | PACKAGES = ['simplemodule'] 24 | 25 | return appfactory('myapp', conf, load=load) 26 | 27 | 28 | def test_factory(): 29 | """Test CLI factory method with/without app context.""" 30 | cli = clifactory(create_app) 31 | 32 | # Without app context 33 | runner = CliRunner() 34 | result = runner.invoke(cli, ['testsimple']) 35 | assert result.exit_code == 0 36 | assert result.output == 'Test Simple\n' 37 | 38 | # With app context 39 | runner = CliRunner() 40 | result = runner.invoke(cli, ['testapp']) 41 | assert result.exit_code == 0 42 | assert result.output == 'Test myapp\n' 43 | 44 | 45 | def test_cli_module_only_testing(): 46 | """Test that CLI module can be tested standalone.""" 47 | from simplemodule.cli import testsimple, testapp 48 | 49 | runner = CliRunner() 50 | result = runner.invoke(testsimple, []) 51 | assert result.exit_code == 0 52 | assert result.output == 'Test Simple\n' 53 | 54 | # Testing click applications which needs the Flask app context requires you 55 | # to manually create a ScriptInfo object. 56 | obj = ScriptInfo(create_app=lambda info: Flask('anotherapp')) 57 | 58 | runner = CliRunner() 59 | result = runner.invoke(testapp, [], obj=obj) 60 | assert result.exit_code == 0 61 | assert result.output == 'Test anotherapp\n' 62 | -------------------------------------------------------------------------------- /tests/test_ext.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Flask-AppFactory 4 | # Copyright (C) 2015 CERN. 5 | # 6 | # Flask-AppFactory is free software; you can redistribute it and/or 7 | # modify it under the terms of the Revised BSD License; see LICENSE 8 | # file for more details. 9 | 10 | """Test app factory.""" 11 | 12 | from __future__ import absolute_import, print_function, unicode_literals 13 | 14 | from flask_appfactory import appfactory 15 | 16 | 17 | def test_jinja2_ext(): 18 | """Test Jinja2 extension.""" 19 | class conf: 20 | PACKAGES = ['simplemodule', 'simplemodule2'] 21 | EXTENSIONS = ['flask_appfactory.ext.jinja2'] 22 | JINJA2_EXTENSIONS = ['jinja2.ext.do'] 23 | 24 | app = appfactory("dummyapp", conf) 25 | 26 | with app.test_client() as c: 27 | c.get('/simplemodule').data == "SIMPLEMODULE" 28 | c.get('/simplemodule2') == "SIMPLEMODULE" 29 | 30 | # Test reverse package order 31 | class conf: 32 | PACKAGES = ['simplemodule2', 'simplemodule'] 33 | EXTENSIONS = ['flask_appfactory.ext.jinja2'] 34 | 35 | app = appfactory("dummyapp", conf) 36 | 37 | with app.test_client() as c: 38 | c.get('/simplemodule').data == "SIMPLEMODULE2" 39 | c.get('/simplemodule2') == "SIMPLEMODULE2" 40 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # This file is part of Flask-AppFactory 2 | # Copyright (C) 2015 CERN. 3 | # 4 | # Flask-AppFactory is free software; you can redistribute it and/or 5 | # modify it under the terms of the Revised BSD License; see LICENSE 6 | # file for more details. 7 | 8 | # 9 | # Tox configuration file 10 | # 11 | 12 | [tox] 13 | envlist = {py27,py33,py34}-{lowest,latest,devel} 14 | 15 | [testenv] 16 | commands = 17 | python setup.py test 18 | 19 | deps = 20 | pytest 21 | 22 | lowest: Flask==0.10 23 | lowest: click==2.0 24 | lowest: Flask-CLI==0.2.0 25 | lowest: Flask-Registry==0.2.0 26 | lowest: Flask-CeleryExt==0.1.0 27 | latest: Flask 28 | latest: click 29 | latest: Flask-CLI 30 | latest: Flask-Registry 31 | latest: Flask-CeleryExt 32 | devel: git+https://github.com/mitsuhiko/flask.git 33 | devel: git+https://github.com/mitsuhiko/click.git 34 | devel: git+https://github.com/inveniosoftware/flask-celeryext.git 35 | devel: git+https://github.com/inveniosoftware/flask-cli.git 36 | devel: git+https://github.com/inveniosoftware/flask-registry.git 37 | 38 | --------------------------------------------------------------------------------