├── .gitignore ├── .travis.yml ├── CHANGES ├── README.rst ├── cookiecutter.json ├── dev-requirements.txt ├── scent.py └── {{cookiecutter.repo_name}} ├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── CHANGES ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── TODO ├── docs ├── Makefile ├── api.rst ├── authors.rst ├── conf.py ├── contributing.rst ├── history.rst ├── index.rst ├── installation.rst ├── make.bat ├── requirements.txt ├── roadmap.rst └── usage.rst ├── manage.py ├── requirements.txt ├── run-tests.py ├── settings ├── default.yml └── testing.yml ├── setup.py ├── tox.ini └── {{cookiecutter.repo_name}} ├── __about__.py ├── __init__.py ├── _compat.py ├── blueprint_example.py ├── core.py ├── exc.py ├── settings.py ├── testsuite ├── __init__.py ├── application.py ├── helpers.py └── {{cookiecutter.repo_name}}.py ├── util.py └── {{cookiecutter.repo_name}}.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | 37 | # Complexity 38 | output/*.html 39 | output/*/index.html 40 | 41 | # Sphinx 42 | docs/_build 43 | 44 | # Cookiecutter 45 | output/ 46 | boilerplate/ 47 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | if: (type = push AND branch IN (master)) OR (type = pull_request) 4 | python: 5 | - "3.7" 6 | - "2.7" 7 | - "pypy" 8 | 9 | before_install: 10 | - pip install cookiecutter 11 | - cookiecutter . --no-input 12 | 13 | # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors 14 | install: pip install -r ./boilerplate/requirements.txt 15 | 16 | script: python ./boilerplate/run-tests.py 17 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | ========= 2 | Changelog 3 | ========= 4 | 5 | Here you can find the recent changes to cookiecutter-flask-pythonic 6 | 7 | 2015-05-08 8 | ---------- 9 | 10 | - [internal] Application object now raises a more helpful error when 11 | registering a non-blueprint app object. 12 | - [tests] separate Application TestCase for testing new testcase. 13 | - [defaults] New core blueprint 14 | - [defaults] Default landing page for core blueprint 15 | 16 | 2015-05-03 17 | ---------- 18 | 19 | - See Features in README.rd 20 | - Forked from tony/cookiecutter-pypackage-pythonic 21 | 22 | 2015-03-26 23 | ---------- 24 | 25 | - Add changelog 26 | - Update to use the ``__about__`` package metadata convention from 27 | `pypa/warehouse`_. 28 | - Update ``README.rst`` example of how package meta data is eval'd. 29 | - Deprecate ``package_metadata.py``. 30 | - For ``__future__`` imports, do parens instead of ``\`` for line breaks. 31 | 32 | .. _pypa/warehouse: https://github.com/pypa/warehouse 33 | 34 | Forked 35 | ------ 36 | 37 | Forked from `audreyr/cookiecutter-pypackage`_ on February 2, 2014. 38 | 39 | Testing 40 | ~~~~~~~ 41 | 42 | - `flask`_/`werkzeug`_-style testsuites. See `werkzeug testsuite`_ on 43 | github. 44 | - Use ``run-tests.py`` to run all tests, or pass in arguments to test a 45 | particular ``TestSuite``, ``TestCase`` or ``Test``: 46 | 47 | .. code-block:: bash 48 | 49 | $ ./run-tests.py 50 | $ ./run-tests.py yourpackage 51 | $ ./run-tests.py repo_name.repo_name # package_name.TestSuite 52 | $ ./run-tests.py yourpackage.testsuite.test_something 53 | $ ./run-tests.py testsuite.test_something 54 | $ ./run-tests.py test_something 55 | $ ./run-tests.py test_something test_something_docstring 56 | 57 | - ``setup.py`` downloads ``unittest2`` for python 2.6. 58 | 59 | Python 2.7+3.3 60 | ~~~~~~~~~~~~~~ 61 | 62 | - .. code-block:: python 63 | 64 | from __future__ import absolute_import, division, print_function, \ 65 | with_statement, unicode_literals 66 | - ``repo_name/_compat.py`` module (derived from flask, werkzeug and 67 | jinja2.) Why a compatibility module? See Armin Ronacher's post `Porting 68 | to Python 3 Redux`_. 69 | 70 | .. _Porting to Python 3 Redux: http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/ 71 | 72 | Packaging 73 | ~~~~~~~~~ 74 | 75 | - ``repo_name/__init__.py`` + ``package_metadata.py``: Metadata in 76 | ``repo_name/__init__.py`` e.g. ``__title__``, ``__author__`` can be 77 | accessed via: 78 | 79 | .. code-block:: python 80 | 81 | >>> import sys 82 | >>> sys.path.insert(0, path_to_projectroot) # on a one-off basis 83 | >>> from package_metadata import p 84 | >>> print(p.title) 85 | Your project name 86 | 87 | Keeps ``setup.py`` and ``doc/conf.py`` in sync with package metadata. 88 | pypi and readthedocs distributions build off the latest package data. 89 | 90 | This method avoids cost of tokenizing and importing python file and 91 | avoids encountering potential import errors that may arise. It simple 92 | opens the files and pulls data by regex. 93 | 94 | Derived from: `How can I get the version defined in setup.py setuptools 95 | in my package?`_ on StackOverflow. 96 | 97 | - Relative imports in ``repo_name/__init__.py``. 98 | - Relative imports in ``repo_name/testsuite/__init__.py``. 99 | - Relative imports in ``repo_name/testsuite/{{ cookiecutter.package_name }}.py``. 100 | 101 | Docs 102 | ~~~~ 103 | 104 | - ``README.rst`` reStructuredText table for project information. 105 | - vim modelines for ``rst`` in ``TODO`` and ``CHANGELOG``. 106 | - ``docs/requirements.txt``, which can be targetted to install `sphinx 107 | changelog`_ package on `ReadTheDocs`. It will also trigger `-r 108 | ../requirements.txt`. 109 | - `sphinx changelog`_ module, (imitation of `sqlalchemy`_, see `sqlalchemy 110 | changelog`_) 111 | - Add ``TODO`` and ``docs/roadmap.rst``. 112 | - Rename ``CHANGELOG.rst`` -> ``CHANGELOG``. 113 | - Add ``docs/api.rst`` for documentation of API code. Initial class 114 | imported with docstring example. 115 | - Automatically generate header spacing based on length of 116 | ``cookiecutter`` variables. 117 | 118 | Example data 119 | ~~~~~~~~~~~~ 120 | 121 | - Example TestCase. 122 | - Example Class w/ docstrings. 123 | 124 | .. _flask: http://flask.pocoo.org 125 | .. _werkzeug: http://werkzeug.pocoo.org 126 | .. _werkzeug testsuite: https://github.com/mitsuhiko/werkzeug/tree/master/werkzeug/testsuite 127 | .. _sqlalchemy: http://sqlalchemy.org 128 | .. _sqlalchemy changelog: http://docs.sqlalchemy.org/en/latest/changelog/ 129 | .. _sphinx changelog: https://pypi.python.org/pypi/changelog 130 | .. _cookiecutter: https://github.com/audreyr/cookiecutter 131 | .. _cookiecutter-pypackage: https://github.com/audreyr/cookiecutter-pypackage 132 | .. _ 133 | 134 | .. _audreyr/cookiecutter-pypackage: https://github.com/audreyr/cookiecutter-pypackage 135 | 136 | .. # vim: set filetype=rst: 137 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | cookiecutter-flask-pythonic 3 | =========================== 4 | 5 | .. image:: https://img.shields.io/travis/tony/cookiecutter-flask-pythonic.svg 6 | :target: https://travis-ci.org/tony/cookiecutter-flask-pythonic 7 | 8 | Forked from `cookiecutter`_ template for a Python package. Forked from 9 | `audreyr/cookiecutter-pypackage`_ + 10 | `tony/cookiecutter-pypackage-pythonic`_. 11 | 12 | It is inspired by `flask`_ and `werkzeug`_'s project style patterns. It is 13 | used on the `tmuxp`_, `cihai-python`_ and `vcspull`_ projects. The app 14 | object is based on early versions `pypa/warehouse`_, when it used 15 | `werkzeug`_ (see `warehouse's old app object`_). 16 | 17 | - Free software: BSD license 18 | - Vanilla testing setup with `unittest` and `python setup.py test` 19 | - Travis-CI_: Ready for Travis Continuous Integration testing 20 | - Tox_ testing: Setup to easily test for Python 2.6, 2.7, 3.3 21 | - Sphinx_ docs: Documentation ready for generation with, for example, 22 | ReadTheDocs_. Note: You can install sphinx docs requirements with 23 | ``$ pip install -r docs/requirements.txt``. 24 | 25 | .. _warehouse's old app object: https://github.com/pypa/warehouse/blob/werkzeug/warehouse/application.py#L68 26 | 27 | Additions and changes 28 | --------------------- 29 | 30 | - (feature) app factory can be configured with ini, yaml or json 31 | (`pypa/warehouse`_, `cihai`_ style) and by specifying options via CLI. 32 | Flask-Script only supports ``-c config_file``. Application factory can 33 | be wired to support option args and config file (pypa/warehouse, cihai) 34 | - (feature / removal) - use `flask testing client`_ for tests, remove 35 | dependency on `flask-testing`_. 36 | - (feature) config file declaratively pulls blueprints from python paths 37 | add them in the app factory. 38 | - (coverage) unittests against flask api, blueprint example module. 39 | 40 | .. _pypa/warehouse: https://github.com/pypa/warehouse 41 | .. _cihai: https://github.com/cihai/cihai-python 42 | .. _flask testing client: http://flask.pocoo.org/docs/testing/ 43 | .. _flask-testing: https://pythonhosted.org/Flask-Testing/ 44 | 45 | Testing 46 | ~~~~~~~ 47 | 48 | - `flask`_/`werkzeug`_-style testsuites. See `werkzeug testsuite`_ on 49 | github. 50 | - Use ``run-tests.py`` to run all tests, or pass in arguments to test a 51 | particular ``TestSuite``, ``TestCase`` or ``Test``: 52 | 53 | .. code-block:: bash 54 | 55 | $ ./run-tests.py 56 | $ ./run-tests.py yourpackage 57 | $ ./run-tests.py repo_name.repo_name # package_name.TestSuite 58 | $ ./run-tests.py yourpackage.testsuite.test_something 59 | $ ./run-tests.py testsuite.test_something 60 | $ ./run-tests.py test_something 61 | $ ./run-tests.py test_something test_something_docstring 62 | 63 | - ``setup.py`` downloads ``unittest2`` for python 2.6. 64 | 65 | Python 2.7+3.3 66 | ~~~~~~~~~~~~~~ 67 | 68 | - .. code-block:: python 69 | 70 | from __future__ import absolute_import, division, print_function, \ 71 | with_statement, unicode_literals 72 | - ``repo_name/_compat.py`` module (derived from flask, werkzeug and 73 | jinja2.) Why a compatibility module? See Armin Ronacher's post `Porting 74 | to Python 3 Redux`_. 75 | 76 | .. _Porting to Python 3 Redux: http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/ 77 | 78 | Packaging 79 | ~~~~~~~~~ 80 | 81 | - ``repo_name/__init__.py`` + ``repo_name/__about__.py``: Metadata in 82 | ``repo_name/__init__.py`` e.g. ``__title__``, ``__author__`` can be 83 | accessed via: 84 | 85 | .. code-block:: python 86 | 87 | >>> about = {} 88 | >>> with open("repo_name/__about__.py") as fp: 89 | >>> exec(fp.read(), about) 90 | >>> print(about['__title__']) 91 | Your project name 92 | 93 | Keeps ``setup.py`` and ``doc/conf.py`` in sync with package metadata. 94 | pypi and readthedocs distributions build off the latest package data. 95 | 96 | This method avoids cost of tokenizing and importing python file and 97 | avoids encountering potential import errors that may arise. It simply 98 | opens a vanilla python file and evals it. 99 | 100 | Derived from `pypa/warehouse`_. 101 | 102 | - Relative imports in ``repo_name/__init__.py``. 103 | - Relative imports in ``repo_name/testsuite/__init__.py``. 104 | - Relative imports in ``repo_name/testsuite/{{ cookiecutter.package_name }}.py``. 105 | 106 | .. _pypa/warehouse: https://github.com/pypa/warehouse 107 | 108 | Docs 109 | ~~~~ 110 | 111 | - ``README.rst`` reStructuredText table for project information. 112 | - vim modelines for ``rst`` in ``TODO`` and ``CHANGELOG``. 113 | - ``docs/requirements.txt``, which can be targetted to install `sphinx 114 | changelog`_ package on `ReadTheDocs`. It will also trigger `-r 115 | ../requirements.txt`. 116 | - `sphinx changelog`_ module, (imitation of `sqlalchemy`_, see `sqlalchemy 117 | changelog`_) 118 | - Add ``TODO`` and ``docs/roadmap.rst``. 119 | - Rename ``CHANGELOG.rst`` -> ``CHANGELOG``. 120 | - Add ``docs/api.rst`` for documentation of API code. Initial class 121 | imported with docstring example. 122 | - Automatically generate header spacing based on length of 123 | ``cookiecutter`` variables. 124 | 125 | Example data 126 | ~~~~~~~~~~~~ 127 | 128 | - Example TestCase. 129 | - Example Class w/ docstrings. 130 | 131 | .. _flask: http://flask.pocoo.org 132 | .. _werkzeug: http://werkzeug.pocoo.org 133 | .. _werkzeug testsuite: https://github.com/mitsuhiko/werkzeug/tree/master/werkzeug/testsuite 134 | .. _sqlalchemy: http://sqlalchemy.org 135 | .. _sqlalchemy changelog: http://docs.sqlalchemy.org/en/latest/changelog/ 136 | .. _sphinx changelog: https://pypi.python.org/pypi/changelog 137 | .. _cookiecutter: https://github.com/audreyr/cookiecutter 138 | .. _cookiecutter-pypackage: https://github.com/audreyr/cookiecutter-pypackage 139 | .. _How can I get the version defined in setup.py setuptools in my package?: http://stackoverflow.com/a/3619714 140 | 141 | Usage 142 | ----- 143 | 144 | Install `cookiecutter`_: 145 | 146 | .. code-block:: bash 147 | 148 | $ sudo pip install cookiecutter 149 | 150 | Generate a Python package project: 151 | 152 | .. code-block:: bash 153 | 154 | $ cookiecutter https://github.com/tony/cookiecutter-flask-pythonic.git 155 | 156 | Then: 157 | 158 | * Create a repo and put it there. 159 | * Add the repo to your `Travis-CI`_ account. 160 | * Add the repo to your `ReadTheDocs`_ account + turn on the ReadTheDocs 161 | service hook. 162 | * Release your package the standard Python way. Here's a release 163 | checklist: https://gist.github.com/audreyr/5990987 164 | 165 | Not Exactly What You Want? 166 | -------------------------- 167 | 168 | Don't worry, you have options: 169 | 170 | Similar Cookiecutter Templates 171 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 172 | 173 | * `audreyr/cookiecutter-pypackage`_: Forked from 174 | ``tony/cookiecutter-pypackage-pythonic``. 175 | * `tony/cookiecutter-pypackage-pythonic`_: This package's fork. Includes 176 | python 2 + 3, sphinx, unit tests, and more. 177 | * `sloria/cookiecutter-flask`_: A flask template with Bootstrap 3, asset 178 | bundling + minification, starter templates and user accounts. 179 | * Also see the `network`_ and `family tree`_ for this repo. (If you find 180 | anything that should be listed here, please add it and send a pull 181 | request!) 182 | 183 | .. _tony/cookiecutter-pypackage-pythonic: https://github.com/tony/cookiecutter-pypackage-pythonic 184 | .. _sloria/cookiecutter-flask: https://github.com/sloria/cookiecutter-flask 185 | 186 | Testing 187 | ~~~~~~~ 188 | 189 | .. code-block:: shell 190 | 191 | $ pip install -r dev-requirements.txt 192 | $ sniffer 193 | # edit any file in {{cookiecutter.repo_name}} 194 | # todo: add run-tests.py for one-time runs 195 | 196 | Fork This / Create Your Own 197 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 198 | 199 | If you have differences in your preferred setup, I encourage you to fork this 200 | to create your own version. Or create your own; it doesn't strictly have to 201 | be a fork. 202 | 203 | * Once you have your own version working, add it to the Similar Cookiecutter 204 | Templates list above with a brief description. 205 | 206 | * It's up to you whether or not to rename your fork/own version. Do whatever 207 | you think sounds good. 208 | 209 | Or Submit a Pull Request 210 | ~~~~~~~~~~~~~~~~~~~~~~~~ 211 | 212 | I also accept pull requests on this, if they're small, atomic, and if they 213 | make my own packaging experience better. 214 | 215 | 216 | .. _Travis-CI: http://travis-ci.org/ 217 | .. _Tox: http://testrun.org/tox/ 218 | .. _Sphinx: http://sphinx-doc.org/ 219 | .. _ReadTheDocs: https://readthedocs.org/ 220 | .. _`Nekroze/cookiecutter-pypackage`: https://github.com/Nekroze/cookiecutter-pypackage 221 | .. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage 222 | .. _`network`: https://github.com/audreyr/cookiecutter-pypackage/network 223 | .. _`family tree`: https://github.com/audreyr/cookiecutter-pypackage/network/members 224 | .. _tmuxp: https://github.com/tony/tmuxp 225 | .. _vcspull: https://github.com/tony/vcspull 226 | .. _cihai-python: https://github.com/cihai/cihai-python 227 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "full_name": "Your Name", 3 | "email": "youremail@domain.com", 4 | "github_username": "github_username", 5 | "project_name": "Python Boilerplate", 6 | "repo_name": "boilerplate", 7 | "project_short_description": "Description of your python package", 8 | "release_date": "2014-02-22", 9 | "year": "2014", 10 | "version": "0.1.0", 11 | "license": "BSD" 12 | } 13 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | git+https://github.com/tony/cookiecutter@output_dir#eggname=cookiecutter 2 | sniffer==0.4.1 3 | -------------------------------------------------------------------------------- /scent.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import os 4 | import sys 5 | import termstyle 6 | 7 | import pip 8 | 9 | from cookiecutter.main import cookiecutter 10 | 11 | from sniffer.api import file_validator, runnable 12 | 13 | project_name = "boilerplate" 14 | extra_context = { 15 | "project_name": project_name, 16 | "repo_name": project_name, 17 | } 18 | 19 | current_dir = cookiecutter_dir = os.path.dirname(os.path.abspath(__file__)) 20 | 21 | 22 | # you can customize the pass/fail colors like this 23 | pass_fg_color = termstyle.green 24 | pass_bg_color = termstyle.bg_default 25 | fail_fg_color = termstyle.red 26 | fail_bg_color = termstyle.bg_default 27 | 28 | # All lists in this variable will be under surveillance for changes. 29 | watch_paths = ['{{cookiecutter.repo_name}}/'] 30 | 31 | @file_validator 32 | def py_files(filename): 33 | return filename.endswith('.py') and not os.path.basename(filename).startswith('.') and filename != ".tmuxp" 34 | 35 | 36 | from contextlib import contextmanager 37 | import tempfile 38 | import shutil 39 | 40 | @contextmanager 41 | def TemporaryDirectory(*args, **kwargs): 42 | _file = tempfile.mkdtemp(*args, **kwargs) 43 | try: 44 | yield _file 45 | finally: 46 | shutil.rmtree(_file) 47 | 48 | 49 | @runnable 50 | def execute_nose(*args): 51 | with TemporaryDirectory() as temp_dir: 52 | try: 53 | boilerplate = None 54 | project_dir = os.path.join(temp_dir, project_name) 55 | requirements = os.path.join(project_dir, 'requirements.txt') 56 | 57 | cookiecutter( 58 | cookiecutter_dir, no_input=True, extra_context=extra_context, 59 | output_dir=temp_dir 60 | ) 61 | 62 | if project_dir not in sys.path: 63 | sys.path.append(project_dir) 64 | 65 | try: 66 | import boilerplate 67 | 68 | from boilerplate.testsuite import main 69 | except ImportError: 70 | pip.main(['install', '-r', requirements]) 71 | import boilerplate 72 | 73 | from boilerplate.testsuite import main 74 | 75 | return main() 76 | except SystemExit as x: 77 | if hasattr(x, 'message'): 78 | print("Found error {0}: {1}".format(x.code, x.message)) 79 | return not x.code 80 | else: 81 | return 1 82 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | 37 | # Complexity 38 | output/*.html 39 | output/*/index.html 40 | 41 | # Sphinx 42 | docs/_build 43 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | 3 | language: python 4 | 5 | if: (type = push AND branch IN (master)) OR (type = pull_request) 6 | 7 | python: 8 | - "3.3" 9 | - "2.7" 10 | - "2.6" 11 | - "pypy" 12 | 13 | # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors 14 | install: pip install -r requirements.txt 15 | 16 | # command to run tests, e.g. python setup.py test 17 | script: python setup.py test 18 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Development Lead 6 | ---------------- 7 | 8 | * {{ cookiecutter.full_name }} <{{ cookiecutter.email }}> 9 | 10 | Contributors 11 | ------------ 12 | 13 | None yet. Why not be the first? 14 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/CHANGES: -------------------------------------------------------------------------------- 1 | ========= 2 | Changelog 3 | ========= 4 | 5 | Here you can find the recent changes to {{ cookiecutter.project_name }}.. 6 | 7 | .. changelog:: 8 | :version: dev 9 | :released: Ongoing 10 | 11 | .. change:: 12 | :tags: docs 13 | 14 | Updated CHANGES. 15 | 16 | .. changelog:: 17 | :version: {{ cookiecutter.version }} 18 | :released: {{ cookiecutter.release_date }} 19 | 20 | .. change:: 21 | :tags: project 22 | 23 | First release on PyPi. 24 | 25 | .. todo:: vim: set filetype=rst: 26 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | 5 | Contributions are welcome, and they are greatly appreciated! Every 6 | little bit helps, and credit will always be given. 7 | 8 | You can contribute in many ways: 9 | 10 | Types of Contributions 11 | ---------------------- 12 | 13 | Report Bugs 14 | ~~~~~~~~~~~ 15 | 16 | Report bugs at https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/issues. 17 | 18 | If you are reporting a bug, please include: 19 | 20 | * Your operating system name and version. 21 | * Any details about your local setup that might be helpful in troubleshooting. 22 | * Detailed steps to reproduce the bug. 23 | 24 | Fix Bugs 25 | ~~~~~~~~ 26 | 27 | Look through the GitHub issues for bugs. Anything tagged with "bug" 28 | is open to whoever wants to implement it. 29 | 30 | Implement Features 31 | ~~~~~~~~~~~~~~~~~~ 32 | 33 | Look through the GitHub issues for features. Anything tagged with "feature" 34 | is open to whoever wants to implement it. 35 | 36 | Write Documentation 37 | ~~~~~~~~~~~~~~~~~~~ 38 | 39 | {{ cookiecutter.project_name }} could always use more documentation, whether as part of the 40 | official {{ cookiecutter.project_name }} docs, in docstrings, or even on the web in blog posts, 41 | articles, and such. 42 | 43 | Submit Feedback 44 | ~~~~~~~~~~~~~~~ 45 | 46 | The best way to send feedback is to file an issue at https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/issues. 47 | 48 | If you are proposing a feature: 49 | 50 | * Explain in detail how it would work. 51 | * Keep the scope as narrow as possible, to make it easier to implement. 52 | * Remember that this is a volunteer-driven project, and that contributions 53 | are welcome :) 54 | 55 | Get Started! 56 | ------------ 57 | 58 | Ready to contribute? Here's how to set up `{{ cookiecutter.repo_name }}` for local development. 59 | 60 | 1. Fork the `{{ cookiecutter.repo_name }}` repo on GitHub. 61 | 2. Clone your fork locally:: 62 | 63 | $ git clone git@github.com:your_name_here/{{ cookiecutter.repo_name }}.git 64 | 65 | 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: 66 | 67 | $ mkvirtualenv {{ cookiecutter.repo_name }} 68 | $ cd {{ cookiecutter.repo_name }}/ 69 | $ python setup.py develop 70 | 71 | 4. Create a branch for local development:: 72 | 73 | $ git checkout -b name-of-your-bugfix-or-feature 74 | 75 | Now you can make your changes locally. 76 | 77 | 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: 78 | 79 | $ flake8 {{ cookiecutter.repo_name }} tests 80 | $ python setup.py test 81 | $ tox 82 | 83 | To get flake8 and tox, just pip install them into your virtualenv. 84 | 85 | 6. Commit your changes and push your branch to GitHub:: 86 | 87 | $ git add . 88 | $ git commit -m "Your detailed description of your changes." 89 | $ git push origin name-of-your-bugfix-or-feature 90 | 91 | 7. Submit a pull request through the GitHub website. 92 | 93 | Pull Request Guidelines 94 | ----------------------- 95 | 96 | Before you submit a pull request, check that it meets these guidelines: 97 | 98 | 1. The pull request should include tests. 99 | 2. If the pull request adds functionality, the docs should be updated. Put 100 | your new functionality into a function with a docstring, and add the 101 | feature to the list in README.rst. 102 | 3. The pull request should work for Python 2.6, 2.7, and 3.3, and for PyPy. Check 103 | https://travis-ci.org/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/pull_requests 104 | and make sure that the tests pass for all supported Python versions. 105 | 106 | Tips 107 | ---- 108 | 109 | To run a subset of tests:: 110 | 111 | $ python -m unittest tests.test_{{ cookiecutter.repo_name }} 112 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) {{ cookiecutter.year }}, {{ cookiecutter.full_name }} 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of {{ cookiecutter.project_name }} nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | 14 | Code from other projects 15 | ------------------------ 16 | 17 | Some code on this project is used from Werkzeug and Flask, which are subject 18 | to permissive BSD License. This includes code in ``_compat`` and 19 | ``run-tests.py``, ``testsuite/__init__.py``. 20 | 21 | Flask LICENSE: http://flask.pocoo.org/docs/license/ 22 | Werkzeug LICENSE: https://github.com/mitsuhiko/werkzeug/blob/master/LICENSE 23 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include CONTRIBUTING.rst 3 | include CHANGES 4 | include TODO 5 | include LICENSE 6 | include README.rst 7 | include package_metadata.py 8 | include requirements.txt 9 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean-pyc clean-build docs 2 | 3 | help: 4 | @echo "clean-build - remove build artifacts" 5 | @echo "clean-pyc - remove Python file artifacts" 6 | @echo "lint - check style with flake8" 7 | @echo "test - run tests quickly with the default Python" 8 | @echo "testall - run tests on every Python version with tox" 9 | @echo "coverage - check code coverage quickly with the default Python" 10 | @echo "docs - generate Sphinx HTML documentation, including API docs" 11 | @echo "release - package and upload a release" 12 | @echo "sdist - package" 13 | 14 | clean: clean-build clean-pyc 15 | 16 | clean-build: 17 | rm -fr build/ 18 | rm -fr dist/ 19 | rm -fr *.egg-info 20 | 21 | clean-pyc: 22 | find . -name '*.pyc' -exec rm -f {} + 23 | find . -name '*.pyo' -exec rm -f {} + 24 | find . -name '*~' -exec rm -f {} + 25 | 26 | lint: 27 | flake8 {{ cookiecutter.repo_name }} tests 28 | 29 | test: 30 | python setup.py test 31 | 32 | test-all: 33 | tox 34 | 35 | coverage: 36 | coverage run --source {{ cookiecutter.repo_name }} setup.py test 37 | coverage report -m 38 | coverage html 39 | open htmlcov/index.html 40 | 41 | docs: 42 | rm -f docs/{{ cookiecutter.repo_name }}.rst 43 | rm -f docs/modules.rst 44 | sphinx-apidoc -o docs/ {{ cookiecutter.repo_name }} 45 | $(MAKE) -C docs clean 46 | $(MAKE) -C docs html 47 | open docs/_build/html/index.html 48 | 49 | release: clean 50 | python setup.py sdist upload 51 | 52 | sdist: clean 53 | python setup.py sdist 54 | ls -l dist 55 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/README.rst: -------------------------------------------------------------------------------- 1 | {{ '=' * cookiecutter.project_name|count }} 2 | {{ cookiecutter.project_name }} 3 | {{ '=' * cookiecutter.project_name|count }} 4 | 5 | 6 | .. image:: https://travis-ci.org/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}.png?branch=master 7 | :target: https://travis-ci.org/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }} 8 | 9 | .. image:: https://badge.fury.io/py/{{ cookiecutter.repo_name }}.png 10 | :target: http://badge.fury.io/py/{{ cookiecutter.repo_name }} 11 | 12 | .. image:: https://coveralls.io/repos/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/badge.png?branch=master 13 | :target: https://coveralls.io/r/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}?branch=master 14 | 15 | .. image:: https://pypip.in/d/{{ cookiecutter.repo_name }}/badge.png 16 | :target: https://crate.io/packages/{{ cookiecutter.repo_name }}?version=latest 17 | 18 | ``{{ cookiecutter.repo_name }}`` - {{ cookiecutter.project_short_description }} 19 | 20 | Features 21 | -------- 22 | 23 | * TODO 24 | 25 | ============== ========================================================== 26 | Python support Python 2.7, >= 3.3 27 | Source https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }} 28 | Docs http://{{ cookiecutter.repo_name }}.rtfd.org 29 | Changelog http://{{ cookiecutter.repo_name }}.readthedocs.org/en/latest/history.html 30 | API http://{{ cookiecutter.repo_name }}.readthedocs.org/en/latest/api.html 31 | Issues https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/issues 32 | Travis http://travis-ci.org/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }} 33 | Test coverage https://coveralls.io/r/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }} 34 | pypi https://pypi.python.org/pypi/{{ cookiecutter.repo_name }} 35 | Ohloh https://www.ohloh.net/p/{{ cookiecutter.repo_name }} 36 | License `BSD`_. 37 | git repo .. code-block:: bash 38 | 39 | $ git clone https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}.git 40 | install dev .. code-block:: bash 41 | 42 | $ git clone https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}.git {{ cookiecutter.repo_name }} 43 | $ cd ./{{ cookiecutter.repo_name }} 44 | $ virtualenv .env 45 | $ source .env/bin/activate 46 | $ pip install -e . 47 | tests .. code-block:: bash 48 | 49 | $ python setup.py test 50 | ============== ========================================================== 51 | 52 | .. _BSD: http://opensource.org/licenses/BSD-3-Clause 53 | .. _Documentation: http://{{ cookiecutter.repo_name }}.readthedocs.org/en/latest/ 54 | .. _API: http://{{ cookiecutter.repo_name }}.readthedocs.org/en/latest/api.html 55 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/TODO: -------------------------------------------------------------------------------- 1 | ==== 2 | TODO 3 | ==== 4 | 5 | dev 6 | --- 7 | 8 | - Write the tests 9 | - Write the project 10 | - Release 11 | - Optimize for clarity 12 | - Release 13 | - Optimize for speed 14 | - Release 15 | 16 | .. todo:: vim: set filetype=rst: 17 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(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/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/complexity.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/complexity.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/complexity" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/complexity" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | === 4 | API 5 | === 6 | 7 | .. automodule:: {{ cookiecutter.repo_name }} 8 | :members: 9 | 10 | .. autoclass:: {{ cookiecutter.repo_name | capitalize }} 11 | :members: 12 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # complexity documentation build configuration file, created by 5 | # sphinx-quickstart on Tue Jul 9 22:26:36 2013. 6 | # 7 | # This file is execfile()d with the current directory set to its 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, os 16 | 17 | # If extensions (or modules to document with autodoc) are in another directory, 18 | # add these directories to sys.path here. If the directory is relative to the 19 | # documentation root, use os.path.abspath to make it absolute, like shown here. 20 | #sys.path.insert(0, os.path.abspath('.')) 21 | 22 | # Get the project root dir, which is the parent dir of this 23 | cwd = os.getcwd() 24 | project_root = os.path.dirname(cwd) 25 | 26 | # Insert the project root dir as the first element in the PYTHONPATH. 27 | # This lets us ensure that the source package is imported, and that its 28 | # version is used. 29 | sys.path.insert(0, project_root) 30 | 31 | # package data 32 | about = {} 33 | with open("../{{ cookiecutter.repo_name }}/__about__.py") as fp: 34 | exec(fp.read(), about) 35 | 36 | 37 | import {{ cookiecutter.repo_name }} 38 | 39 | # -- General configuration ----------------------------------------------------- 40 | 41 | # If your documentation needs a minimal Sphinx version, state it here. 42 | #needs_sphinx = '1.0' 43 | 44 | # Add any Sphinx extension module names here, as strings. They can be extensions 45 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 46 | extensions = [ 47 | 'sphinx.ext.autodoc', 48 | 'sphinx.ext.viewcode', 49 | 'sphinx.ext.todo', 50 | 'changelog' 51 | ] 52 | 53 | # Add any paths that contain templates here, relative to this directory. 54 | templates_path = ['_templates'] 55 | 56 | # The suffix of source filenames. 57 | source_suffix = '.rst' 58 | 59 | # The encoding of source files. 60 | #source_encoding = 'utf-8-sig' 61 | 62 | # The master toctree document. 63 | master_doc = 'index' 64 | 65 | # General information about the project. 66 | project = about['__title__'] 67 | copyright = about['__copyright__'] 68 | 69 | # The version info for the project you're documenting, acts as replacement for 70 | # |version| and |release|, also used in various other places throughout the 71 | # built documents. 72 | # 73 | # The short X.Y version. 74 | version = '%s' % ('.'.join(about['__version__'].split('.'))[:2]) 75 | # The full version, including alpha/beta/rc tags. 76 | release = '%s' % (about['__version__']) 77 | 78 | # The language for content autogenerated by Sphinx. Refer to documentation 79 | # for a list of supported languages. 80 | #language = None 81 | 82 | # There are two options for replacing |today|: either, you set today to some 83 | # non-false value, then it is used: 84 | #today = '' 85 | # Else, today_fmt is used as the format for a strftime call. 86 | #today_fmt = '%B %d, %Y' 87 | 88 | # List of patterns, relative to source directory, that match files and 89 | # directories to ignore when looking for source files. 90 | exclude_patterns = ['_build'] 91 | 92 | # The reST default role (used for this markup: `text`) to use for all documents. 93 | #default_role = None 94 | 95 | # If true, '()' will be appended to :func: etc. cross-reference text. 96 | #add_function_parentheses = True 97 | 98 | # If true, the current module name will be prepended to all description 99 | # unit titles (such as .. function::). 100 | #add_module_names = True 101 | 102 | # If true, sectionauthor and moduleauthor directives will be shown in the 103 | # output. They are ignored by default. 104 | #show_authors = False 105 | 106 | # The name of the Pygments (syntax highlighting) style to use. 107 | pygments_style = 'sphinx' 108 | 109 | # A list of ignored prefixes for module index sorting. 110 | #modindex_common_prefix = [] 111 | 112 | # If true, keep warnings as "system message" paragraphs in the built documents. 113 | #keep_warnings = False 114 | 115 | 116 | # -- Options for HTML output --------------------------------------------------- 117 | 118 | # The theme to use for HTML and HTML Help pages. See the documentation for 119 | # a list of builtin themes. 120 | html_theme = 'default' 121 | 122 | # Theme options are theme-specific and customize the look and feel of a theme 123 | # further. For a list of options available for each theme, see the 124 | # documentation. 125 | #html_theme_options = {} 126 | 127 | # Add any paths that contain custom themes here, relative to this directory. 128 | #html_theme_path = [] 129 | 130 | # The name for this set of Sphinx documents. If None, it defaults to 131 | # " v documentation". 132 | #html_title = None 133 | 134 | # A shorter title for the navigation bar. Default is the same as html_title. 135 | #html_short_title = None 136 | 137 | # The name of an image file (relative to this directory) to place at the top 138 | # of the sidebar. 139 | #html_logo = None 140 | 141 | # The name of an image file (within the static path) to use as favicon of the 142 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 143 | # pixels large. 144 | #html_favicon = None 145 | 146 | # Add any paths that contain custom static files (such as style sheets) here, 147 | # relative to this directory. They are copied after the builtin static files, 148 | # so a file named "default.css" will overwrite the builtin "default.css". 149 | html_static_path = ['_static'] 150 | 151 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 152 | # using the given strftime format. 153 | #html_last_updated_fmt = '%b %d, %Y' 154 | 155 | # If true, SmartyPants will be used to convert quotes and dashes to 156 | # typographically correct entities. 157 | #html_use_smartypants = True 158 | 159 | # Custom sidebar templates, maps document names to template names. 160 | #html_sidebars = {} 161 | 162 | # Additional templates that should be rendered to pages, maps page names to 163 | # template names. 164 | #html_additional_pages = {} 165 | 166 | # If false, no module index is generated. 167 | #html_domain_indices = True 168 | 169 | # If false, no index is generated. 170 | #html_use_index = True 171 | 172 | # If true, the index is split into individual pages for each letter. 173 | #html_split_index = False 174 | 175 | # If true, links to the reST sources are added to the pages. 176 | #html_show_sourcelink = True 177 | 178 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 179 | #html_show_sphinx = True 180 | 181 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 182 | #html_show_copyright = True 183 | 184 | # If true, an OpenSearch description file will be output, and all pages will 185 | # contain a tag referring to it. The value of this option must be the 186 | # base URL from which the finished HTML is served. 187 | #html_use_opensearch = '' 188 | 189 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 190 | #html_file_suffix = None 191 | 192 | # Output file base name for HTML help builder. 193 | htmlhelp_basename = '%sdoc' % about['__title__'] 194 | 195 | 196 | # -- Options for LaTeX output -------------------------------------------------- 197 | 198 | latex_elements = { 199 | # The paper size ('letterpaper' or 'a4paper'). 200 | #'papersize': 'letterpaper', 201 | 202 | # The font size ('10pt', '11pt' or '12pt'). 203 | #'pointsize': '10pt', 204 | 205 | # Additional stuff for the LaTeX preamble. 206 | #'preamble': '', 207 | } 208 | 209 | # Grouping the document tree into LaTeX files. List of tuples 210 | # (source start file, target name, title, author, documentclass [howto/manual]). 211 | latex_documents = [ 212 | ('index', '{0}.tex'.format(about['__package_name__']), '{0} Documentation'.format(about['__title__']), 213 | about['__author__'], 'manual'), 214 | ] 215 | 216 | # The name of an image file (relative to this directory) to place at the top of 217 | # the title page. 218 | #latex_logo = None 219 | 220 | # For "manual" documents, if this is true, then toplevel headings are parts, 221 | # not chapters. 222 | #latex_use_parts = False 223 | 224 | # If true, show page references after internal links. 225 | #latex_show_pagerefs = False 226 | 227 | # If true, show URL addresses after external links. 228 | #latex_show_urls = False 229 | 230 | # Documents to append as an appendix to all manuals. 231 | #latex_appendices = [] 232 | 233 | # If false, no module index is generated. 234 | #latex_domain_indices = True 235 | 236 | 237 | # -- Options for manual page output -------------------------------------------- 238 | 239 | # One entry per manual page. List of tuples 240 | # (source start file, name, description, authors, manual section). 241 | man_pages = [ 242 | ('index', about['__package_name__'], '{0} Documentation'.format(about['__title__']), 243 | about['__author__'], 1), 244 | ] 245 | 246 | # If true, show URL addresses after external links. 247 | #man_show_urls = False 248 | 249 | 250 | # -- Options for Texinfo output ------------------------------------------------ 251 | 252 | # Grouping the document tree into Texinfo files. List of tuples 253 | # (source start file, target name, title, author, 254 | # dir menu entry, description, category) 255 | texinfo_documents = [ 256 | ('index', '{0}'.format(about['__package_name__']), '{0} Documentation'.format(about['__title__']), 257 | about['__author__'], about['__package_name__'], about['__description__'], 'Miscellaneous'), 258 | ] 259 | 260 | # Documents to append as an appendix to all manuals. 261 | #texinfo_appendices = [] 262 | 263 | # If false, no module index is generated. 264 | #texinfo_domain_indices = True 265 | 266 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 267 | #texinfo_show_urls = 'footnote' 268 | 269 | # If true, do not generate a @detailmenu in the "Top" node's menu. 270 | #texinfo_no_detailmenu = False 271 | 272 | # Example configuration for intersphinx: refer to the Python standard library. 273 | intersphinx_mapping = {'http://docs.python.org/': None, 'pip': ('http://sphinx.readthedocs.org/en/latest/', None)} 274 | 275 | # section names - optional 276 | changelog_sections = ["general", "rendering", "tests", "docs"] 277 | 278 | # tags to sort on inside of sections - also optional 279 | changelog_inner_tag_sort = ["feature", "bug"] 280 | 281 | # how to render changelog links - these are plain 282 | # python string templates, ticket/pullreq/changeset number goes 283 | # in "%s" 284 | changelog_render_ticket = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/issues/%s" 285 | changelog_render_pullreq = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/pulls/%s" 286 | changelog_render_changeset = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}/commit/%s" 287 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/history.rst: -------------------------------------------------------------------------------- 1 | .. _history: 2 | 3 | ======= 4 | History 5 | ======= 6 | 7 | .. include:: ../CHANGES 8 | :start-line: 5 9 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/index.rst: -------------------------------------------------------------------------------- 1 | .. complexity documentation master file, created by 2 | sphinx-quickstart on Tue Jul 9 22:26:36 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to {{ cookiecutter.project_name }}'s documentation! 7 | {{ '=' * (cookiecutter.project_name|count + 11 + 17) }} 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | installation 15 | usage 16 | api 17 | contributing 18 | authors 19 | roadmap 20 | history 21 | 22 | .. include:: ../README.rst 23 | :start-line: 23 24 | 25 | Indices and tables 26 | ================== 27 | 28 | * :ref:`genindex` 29 | * :ref:`modindex` 30 | * :ref:`search` 31 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | At the command line:: 6 | 7 | $ easy_install {{ cookiecutter.repo_name }} 8 | 9 | Or, if you have virtualenvwrapper installed:: 10 | 11 | $ mkvirtualenv {{ cookiecutter.repo_name }} 12 | $ pip install {{ cookiecutter.repo_name }} 13 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\complexity.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\complexity.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/requirements.txt: -------------------------------------------------------------------------------- 1 | -r ../requirements.txt 2 | changelog==0.4.2 3 | sphinx==1.8.4 4 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/roadmap.rst: -------------------------------------------------------------------------------- 1 | .. _roadmap: 2 | 3 | ======= 4 | Roadmap 5 | ======= 6 | 7 | .. include:: ../TODO 8 | :start-line: 4 9 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/docs/usage.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Usage 3 | ======== 4 | 5 | To use {{ cookiecutter.project_name }} in a project:: 6 | 7 | import {{ cookiecutter.repo_name }} 8 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | 6 | from flask_script import Manager 7 | 8 | from {{ cookiecutter.repo_name }} import {{ cookiecutter.repo_name | capitalize }} 9 | 10 | """If not using Flask-Script:: 11 | 12 | app = {{ cookiecutter.repo_name | capitalize }}.from_cli(sys.argv[1:]) 13 | 14 | Does the trick for retrieving an application object using 15 | pure argparse. But let's hook into Flask-Script's CLI argparse 16 | instance. 17 | """ 18 | 19 | 20 | def app_wrapper(*args, **kwargs): 21 | """App factory returns the :class:`flask.Flask` via ``__call__``, 22 | but because of the way :class:`flask_script.Manager` handles 23 | accepting app objects, this wrapper returns the flask object directly. 24 | 25 | :returns: Flask object build from CLI 26 | :rtype: :class:`flask.Flask` 27 | 28 | """ 29 | 30 | return {{ cookiecutter.repo_name | capitalize }}.from_file(*args, **kwargs).app 31 | 32 | manager = Manager(app_wrapper) 33 | manager.add_option('-c', '--config', dest='config', required=False) 34 | 35 | 36 | @manager.command 37 | def run_server(*args, **kwargs): 38 | {{ cookiecutter.repo_name | capitalize }}.from_file().run() 39 | 40 | 41 | @manager.command 42 | def testing(*args, **kwargs): 43 | print('Run "./run-tests.py" or "python setup.py test".') 44 | 45 | if __name__ == "__main__": 46 | run_server() 47 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/requirements.txt: -------------------------------------------------------------------------------- 1 | flask==1.1.1 2 | flask-script==2.0.6 3 | kaptan==0.5.12 4 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/run-tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from {{ cookiecutter.repo_name }}.testsuite import main 5 | main() 6 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/settings/default.yml: -------------------------------------------------------------------------------- 1 | DEBUG: True 2 | host: localhost 3 | blueprints: 4 | '': '{{cookiecutter.repo_name}}.core.core' 5 | '/blueprint_example': '{{cookiecutter.repo_name}}.blueprint_example.blueprint_example' 6 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/settings/testing.yml: -------------------------------------------------------------------------------- 1 | DEBUG: True 2 | host: localhost 3 | port: 5454 4 | blueprints: 5 | '': '{{cookiecutter.repo_name}}.core.core' 6 | '/blueprint_example': '{{cookiecutter.repo_name}}.blueprint_example.blueprint_example' 7 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | 7 | 8 | try: 9 | from setuptools import setup, find_packages 10 | except ImportError: 11 | from distutils.core import setup, find_packages 12 | 13 | about = {} 14 | with open("{{ cookiecutter.repo_name }}/__about__.py") as fp: 15 | exec(fp.read(), about) 16 | 17 | with open('requirements.txt') as f: 18 | install_reqs = [line for line in f.read().split('\n') if line] 19 | tests_reqs = [] 20 | 21 | if sys.version_info < (2, 7): 22 | install_reqs += ['argparse'] 23 | tests_reqs += ['unittest2'] 24 | 25 | if sys.argv[-1] == 'publish': 26 | os.system('python setup.py sdist upload') 27 | sys.exit() 28 | 29 | if sys.argv[-1] == 'info': 30 | for k, v in about.items(): 31 | print('%s: %s' % (k, v)) 32 | sys.exit() 33 | 34 | readme = open('README.rst').read() 35 | history = open('CHANGES').read().replace('.. :changelog:', '') 36 | 37 | setup( 38 | name=about['__title__'], 39 | version=about['__version__'], 40 | description=about['__description__'], 41 | long_description=readme + '\n\n' + history, 42 | author=about['__author__'], 43 | author_email=about['__email__'], 44 | url='https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.repo_name }}', 45 | packages=find_packages(exclude=['docs']), 46 | include_package_data=True, 47 | install_requires=install_reqs, 48 | tests_require=tests_reqs, 49 | license=about['__license__'], 50 | keywords=about['__title__'], 51 | zip_safe=False, 52 | classifiers=[ 53 | 'Development Status :: 2 - Pre-Alpha', 54 | 'Intended Audience :: Developers', 55 | 'License :: OSI Approved :: {{ cookiecutter.license }} License', 56 | 'Natural Language :: English', 57 | "Programming Language :: Python :: 2", 58 | 'Programming Language :: Python :: 2.6', 59 | 'Programming Language :: Python :: 2.7', 60 | 'Programming Language :: Python :: 3', 61 | 'Programming Language :: Python :: 3.3', 62 | ], 63 | test_suite='{{ cookiecutter.repo_name }}.testsuite', 64 | ) 65 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py26, py27, py33 3 | 4 | [testenv] 5 | setenv = 6 | PYTHONPATH = {toxinidir}:{toxinidir}/{{ cookiecutter.repo_name }} 7 | commands = python setup.py test 8 | deps = 9 | -r{toxinidir}/requirements.txt 10 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/__about__.py: -------------------------------------------------------------------------------- 1 | __title__ = '{{ cookiecutter.project_name }}' 2 | __package_name__ = '{{ cookiecutter.repo_name }}' 3 | __author__ = '{{ cookiecutter.full_name }}' 4 | __description__ = '{{ cookiecutter.project_short_description }}' 5 | __email__ = '{{ cookiecutter.email }}' 6 | __version__ = '{{ cookiecutter.version }}' 7 | __license__ = '{{ cookiecutter.license }}' 8 | __copyright__ = 'Copyright {{ cookiecutter.year }} {{ cookiecutter.full_name }}' 9 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | 5 | {{ cookiecutter.project_name }} 6 | {{ '-' * cookiecutter.project_name|count }} 7 | 8 | {{ cookiecutter.project_short_description }} 9 | 10 | """ 11 | 12 | from __future__ import absolute_import, division, print_function, \ 13 | with_statement, unicode_literals 14 | 15 | from . import exc 16 | 17 | from .{{ cookiecutter.repo_name }} import {{ cookiecutter.repo_name | capitalize }} 18 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/_compat.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | PY2 = sys.version_info[0] == 2 4 | 5 | _identity = lambda x: x 6 | 7 | 8 | if PY2: 9 | unichr = unichr 10 | text_type = unicode 11 | string_types = (str, unicode) 12 | integer_types = (int, long) 13 | from urllib import urlretrieve 14 | 15 | text_to_native = lambda s, enc: s.encode(enc) 16 | 17 | iterkeys = lambda d: d.iterkeys() 18 | itervalues = lambda d: d.itervalues() 19 | iteritems = lambda d: d.iteritems() 20 | 21 | from cStringIO import StringIO as BytesIO 22 | from StringIO import StringIO 23 | import cPickle as pickle 24 | import ConfigParser as configparser 25 | 26 | from itertools import izip, imap 27 | range_type = xrange 28 | 29 | cmp = cmp 30 | 31 | input = raw_input 32 | from string import lower as ascii_lowercase 33 | import urlparse 34 | 35 | def console_to_str(s): 36 | return s.decode('utf_8') 37 | 38 | exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') 39 | 40 | else: 41 | unichr = chr 42 | text_type = str 43 | string_types = (str,) 44 | integer_types = (int, ) 45 | 46 | text_to_native = lambda s, enc: s 47 | 48 | iterkeys = lambda d: iter(d.keys()) 49 | itervalues = lambda d: iter(d.values()) 50 | iteritems = lambda d: iter(d.items()) 51 | 52 | from io import StringIO, BytesIO 53 | import pickle 54 | import configparser 55 | 56 | izip = zip 57 | imap = map 58 | range_type = range 59 | 60 | cmp = lambda a, b: (a > b) - (a < b) 61 | 62 | input = input 63 | from string import ascii_lowercase 64 | import urllib.parse as urllib 65 | import urllib.parse as urlparse 66 | from urllib.request import urlretrieve 67 | 68 | console_encoding = sys.__stdout__.encoding 69 | 70 | def console_to_str(s): 71 | """ From pypa/pip project, pip.backwardwardcompat. License MIT. """ 72 | try: 73 | return s.decode(console_encoding) 74 | except UnicodeDecodeError: 75 | return s.decode('utf_8') 76 | 77 | def reraise(tp, value, tb=None): 78 | if value.__traceback__ is not tb: 79 | raise tp(value.with_traceback(tb)) 80 | raise tp(value) 81 | 82 | 83 | number_types = integer_types + (float,) 84 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/blueprint_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Blueprint for JSON API.""" 4 | 5 | from __future__ import absolute_import, division, print_function, \ 6 | with_statement, unicode_literals 7 | 8 | from flask import Blueprint, request, jsonify 9 | 10 | blueprint_example = Blueprint('blueprint_example', __name__) 11 | 12 | 13 | @blueprint_example.route('/blueprint_example', defaults={'page': 'index'}) 14 | @blueprint_example.route('/') 15 | def show(page): 16 | return 'hi' 17 | 18 | 19 | @blueprint_example.route('/test') 20 | def say_something(): 21 | question = request.args.get('question', type=str) 22 | 23 | response = { 24 | "this": question, 25 | "answer": "Always the same!" 26 | } 27 | 28 | return jsonify(response) 29 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Root application.""" 4 | 5 | from __future__ import absolute_import, division, print_function, \ 6 | with_statement, unicode_literals 7 | 8 | from flask import Blueprint, render_template_string 9 | 10 | core = Blueprint('core', __name__) 11 | 12 | 13 | @core.route('/', defaults={'page': 'index'}) 14 | def show(page): 15 | """Borrowed from DEFAULT_URLCONF_TEMPLATE, Django 1.8, License BSD.""" 16 | 17 | {% raw %} 18 | WELCOME_TPL = """ 19 | 20 | 21 | 22 | {{ title }} 23 | 47 | 48 | 49 |
50 |

{{ heading }}

51 |

{{ subheading }}

52 |
53 |
54 | {{ instructions }} 55 |

56 |

57 |
58 |
59 | {{ explanation }} 60 |

61 |

62 |
63 | 64 | """ 65 | {% endraw %} 66 | 67 | return render_template_string( 68 | WELCOME_TPL, 69 | heading="You've made it!", 70 | title="You've made it!", 71 | subheading="A modular flask experience.", 72 | instructions="Check out " 73 | "git-pull.com " 74 | "for instructions, updates and more pythonic, golang, saas " 75 | "and POSIX goodness. Daily reminder:

" 76 | "

import this
" 77 | "\"C is truth.\"", 78 | explanation = "You are a winner, and the chosen one." 79 | ) 80 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/exc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Exceptions.""" 3 | 4 | from __future__ import absolute_import, division, print_function, \ 5 | with_statement, unicode_literals 6 | 7 | from ._compat import text_type 8 | 9 | 10 | APP_BLUEPRINT_LOAD_EXCEPTION_MSG = """ 11 | You were not registering a Flask blueprint! Are you sure you've fully 12 | specified the *blueprint object* and not just the python module? 13 | 14 | Assuming foo_module.py or foo_module/__init__.py 15 | 16 | and the_blueprint = Blueprint('foo', __name__) 17 | 18 | ``foo_module`` is a module, that's not a blueprint object. 19 | ``foo_module.the_blueprint`` would be the blueprint! 20 | 21 | http://stackoverflow.com/q/26550180 22 | """ 23 | 24 | class AppException(Exception): 25 | 26 | """Base Exception for App Errors.""" 27 | 28 | 29 | class AppBlueprintLoadException(AppException): 30 | 31 | def __str__(self): 32 | return text_type(self.message) + APP_BLUEPRINT_LOAD_EXCEPTION_MSG 33 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | current_path = os.path.abspath(os.path.join( 6 | os.path.dirname(__file__), 7 | )) 8 | parent_path = os.path.dirname(current_path) 9 | settings_path = os.path.join(parent_path, 'settings') 10 | default_config = os.path.join(settings_path, 'default.yml') 11 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/testsuite/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Tests for {{ cookiecutter.project_name }}.""" 5 | from __future__ import ( 6 | absolute_import, division, print_function, with_statement, 7 | unicode_literals 8 | ) 9 | 10 | from .._compat import text_type, string_types, PY2, reraise 11 | 12 | try: 13 | import unittest2 as unittest 14 | except ImportError: # Python 2.7 15 | import unittest 16 | import logging 17 | import sys 18 | import pkgutil 19 | 20 | 21 | class ImportStringError(ImportError): 22 | """Provides information about a failed :func:`import_string` attempt.""" 23 | 24 | #: String in dotted notation that failed to be imported. 25 | import_name = None 26 | #: Wrapped exception. 27 | exception = None 28 | 29 | def __init__(self, import_name, exception): 30 | self.import_name = import_name 31 | self.exception = exception 32 | 33 | msg = ( 34 | 'import_string() failed for %r. Possible reasons are:\n\n' 35 | '- missing __init__.py in a package;\n' 36 | '- package or module path not included in sys.path;\n' 37 | '- duplicated package or module name taking precedence in ' 38 | 'sys.path;\n' 39 | '- missing module, class, function or variable;\n\n' 40 | 'Debugged import:\n\n%s\n\n' 41 | 'Original exception:\n\n%s: %s') 42 | 43 | name = '' 44 | tracked = [] 45 | for part in import_name.replace(':', '.').split('.'): 46 | name += (name and '.') + part 47 | imported = import_string(name, silent=True) 48 | if imported: 49 | tracked.append((name, getattr(imported, '__file__', None))) 50 | else: 51 | track = ['- %r found in %r.' % (n, i) for n, i in tracked] 52 | track.append('- %r not found.' % name) 53 | msg = msg % (import_name, '\n'.join(track), 54 | exception.__class__.__name__, str(exception)) 55 | break 56 | 57 | ImportError.__init__(self, msg) 58 | 59 | def __repr__(self): 60 | return '<%s(%r, %r)>' % (self.__class__.__name__, self.import_name, 61 | self.exception) 62 | 63 | 64 | def import_string(import_name, silent=False): 65 | """Imports an object based on a string. This is useful if you want to 66 | use import paths as endpoints or something similar. An import path can 67 | be specified either in dotted notation (``xml.sax.saxutils.escape``) 68 | or with a colon as object delimiter (``xml.sax.saxutils:escape``). 69 | 70 | If `silent` is True the return value will be `None` if the import fails. 71 | 72 | :param import_name: the dotted name for the object to import. 73 | :param silent: if set to `True` import errors are ignored and 74 | `None` is returned instead. 75 | :return: imported object 76 | """ 77 | #XXX: py3 review needed 78 | assert isinstance(import_name, string_types) 79 | # force the import name to automatically convert to strings 80 | import_name = str(import_name) 81 | try: 82 | if ':' in import_name: 83 | module, obj = import_name.split(':', 1) 84 | elif '.' in import_name: 85 | module, obj = import_name.rsplit('.', 1) 86 | else: 87 | return __import__(import_name) 88 | # __import__ is not able to handle unicode strings in the fromlist 89 | # if the module is a package 90 | if PY2 and isinstance(obj, unicode): 91 | obj = obj.encode('utf-8') 92 | try: 93 | return getattr(__import__(module, None, None, [obj]), obj) 94 | except (ImportError, AttributeError): 95 | # support importing modules not yet set up by the parent module 96 | # (or package for that matter) 97 | modname = module + '.' + obj 98 | __import__(modname) 99 | return sys.modules[modname] 100 | except ImportError as e: 101 | if not silent: 102 | reraise( 103 | ImportStringError, 104 | ImportStringError(import_name, e), 105 | sys.exc_info()[2]) 106 | 107 | 108 | def find_modules(import_path, include_packages=False, recursive=False): 109 | """Find all the modules below a package. This can be useful to 110 | automatically import all views / controllers so that their metaclasses / 111 | function decorators have a chance to register themselves on the 112 | application. 113 | 114 | Packages are not returned unless `include_packages` is `True`. This can 115 | also recursively list modules but in that case it will import all the 116 | packages to get the correct load path of that module. 117 | 118 | :param import_name: the dotted name for the package to find child modules. 119 | :param include_packages: set to `True` if packages should be returned, too. 120 | :param recursive: set to `True` if recursion should happen. 121 | :return: generator 122 | """ 123 | module = import_string(import_path) 124 | path = getattr(module, '__path__', None) 125 | if path is None: 126 | raise ValueError('%r is not a package' % import_path) 127 | basename = module.__name__ + '.' 128 | for importer, modname, ispkg in pkgutil.iter_modules(path): 129 | modname = basename + modname 130 | if ispkg: 131 | if include_packages: 132 | yield modname 133 | if recursive: 134 | for item in find_modules(modname, include_packages, True): 135 | yield item 136 | else: 137 | yield modname 138 | 139 | 140 | def iter_suites(package): 141 | """Yields all testsuites.""" 142 | for module in find_modules(package, include_packages=True): 143 | mod = __import__(module, fromlist=['*']) 144 | if hasattr(mod, 'suite'): 145 | yield mod.suite() 146 | 147 | 148 | def find_all_tests(suite): 149 | """Yields all the tests and their names from a given suite.""" 150 | suites = [suite] 151 | while suites: 152 | s = suites.pop() 153 | try: 154 | suites.extend(s) 155 | except TypeError: 156 | yield s, '%s.%s.%s' % ( 157 | s.__class__.__module__, 158 | s.__class__.__name__, 159 | s._testMethodName 160 | ) 161 | 162 | 163 | class BetterLoader(unittest.TestLoader): 164 | """A nicer loader that solves two problems. First of all we are setting 165 | up tests from different sources and we're doing this programmatically 166 | which breaks the default loading logic so this is required anyways. 167 | Secondly this loader has a nicer interpolation for test names than the 168 | default one so you can just do ``run-tests.py ViewTestCase`` and it 169 | will work. 170 | """ 171 | 172 | def getRootSuite(self): 173 | return suite() 174 | 175 | def loadTestsFromName(self, name, module=None): 176 | root = self.getRootSuite() 177 | if name == 'suite': 178 | return root 179 | 180 | all_tests = [] 181 | for testcase, testname in find_all_tests(root): 182 | if testname == name or \ 183 | testname.endswith('.' + name) or \ 184 | ('.' + name + '.') in testname or \ 185 | testname.startswith(name + '.'): 186 | all_tests.append(testcase) 187 | 188 | if not all_tests: 189 | raise LookupError('could not find test case for "%s"' % name) 190 | 191 | if len(all_tests) == 1: 192 | return all_tests[0] 193 | rv = unittest.TestSuite() 194 | for test in all_tests: 195 | rv.addTest(test) 196 | return rv 197 | 198 | 199 | def suite(): 200 | """A testsuite that has all the Flask tests. You can use this 201 | function to integrate the Flask tests into your own testsuite 202 | in case you want to test that monkeypatches to Flask do not 203 | break it. 204 | """ 205 | suite = unittest.TestSuite() 206 | for other_suite in iter_suites(__name__): 207 | suite.addTest(other_suite) 208 | return suite 209 | 210 | 211 | def main(): 212 | """Runs the testsuite as command line application.""" 213 | try: 214 | unittest.main(testLoader=BetterLoader(), defaultTest='suite') 215 | except Exception: 216 | import sys 217 | import traceback 218 | traceback.print_exc() 219 | sys.exit(1) 220 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/testsuite/application.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Tests for Application object.""" 5 | 6 | from __future__ import ( 7 | absolute_import, division, print_function, with_statement, 8 | unicode_literals 9 | ) 10 | 11 | import os 12 | 13 | from ..settings import settings_path 14 | from .. import {{ cookiecutter.repo_name }}, exc 15 | from . import unittest 16 | 17 | test_config = os.path.join(settings_path, 'testing.yml') 18 | 19 | class Application(unittest.TestCase): 20 | 21 | def test_registering_module_object_raises_helpful_exception(self): 22 | """Raises more helpful exception in case of http://stackoverflow.com/q/26550180.""" 23 | config = { 24 | "blueprints": { 25 | "/": "{{ cookiecutter.repo_name }}.core" 26 | } 27 | } 28 | 29 | with self.assertRaises(exc.AppBlueprintLoadException): 30 | app = {{ cookiecutter.repo_name }}.{{ cookiecutter.repo_name|capitalize }}(config).app 31 | 32 | 33 | def suite(): 34 | from .helpers import setup_path 35 | setup_path() 36 | suite = unittest.TestSuite() 37 | suite.addTest(unittest.makeSuite(Application)) 38 | return suite 39 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/testsuite/helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tests for {{ cookiecutter.project_name }}.""" 3 | 4 | from __future__ import ( 5 | absolute_import, division, print_function, with_statement, 6 | unicode_literals 7 | ) 8 | 9 | import os 10 | import sys 11 | 12 | from contextlib import contextmanager 13 | 14 | from .._compat import StringIO 15 | 16 | 17 | def add_to_path(path): 18 | """Adds an entry to sys.path if it's not already there. This does 19 | not append it but moves it to the front so that we can be sure it 20 | is loaded. 21 | """ 22 | if not os.path.isdir(path): 23 | raise RuntimeError('Tried to add nonexisting path') 24 | 25 | def _samefile(x, y): 26 | if x == y: 27 | return True 28 | try: 29 | return os.path.samefile(x, y) 30 | except (IOError, OSError, AttributeError): 31 | # Windows has no samefile 32 | return False 33 | sys.path[:] = [x for x in sys.path if not _samefile(path, x)] 34 | sys.path.insert(0, path) 35 | 36 | 37 | def setup_path(): 38 | script_path = os.path.join( 39 | os.path.dirname(__file__), os.pardir, 40 | ) 41 | add_to_path(script_path) 42 | 43 | 44 | def get_datapath(filename): 45 | 46 | return os.path.join( 47 | os.path.dirname(__file__), 'fixtures', filename 48 | ) 49 | 50 | 51 | @contextmanager 52 | def captureStdErr(command, *args, **kwargs): 53 | out, sys.stderr = sys.stderr, StringIO() 54 | command(*args, **kwargs) 55 | sys.stderr.seek(0) 56 | yield sys.stderr.read() 57 | sys.stderr = out 58 | 59 | 60 | @contextmanager 61 | def captureStdOut(command, *args, **kwargs): 62 | out, sys.stdout = sys.stderr, StringIO() 63 | command(*args, **kwargs) 64 | sys.stdout.seek(0) 65 | yield sys.stdout.read() 66 | sys.stdout = out 67 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/testsuite/{{cookiecutter.repo_name}}.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Tests for `{{ cookiecutter.repo_name }}` module.""" 5 | 6 | from __future__ import ( 7 | absolute_import, division, print_function, with_statement, 8 | unicode_literals 9 | ) 10 | 11 | import os 12 | 13 | from flask import json 14 | 15 | from ..settings import settings_path 16 | from .. import {{ cookiecutter.repo_name }} 17 | from . import unittest 18 | 19 | test_config = os.path.join(settings_path, 'testing.yml') 20 | 21 | class {{ cookiecutter.repo_name | capitalize }}(unittest.TestCase): 22 | 23 | def setUp(self): 24 | pass 25 | 26 | def test_something(self): 27 | pass 28 | 29 | def test_something_docstring(self): 30 | """Here is a sample test with a docstring. Hey.""" 31 | self.assertTrue(True) 32 | 33 | def tearDown(self): 34 | pass 35 | 36 | 37 | class BlueprintExample(unittest.TestCase): 38 | 39 | app = None 40 | 41 | def setUp(self): 42 | if not self.app: 43 | app = {{ cookiecutter.repo_name }}.{{ cookiecutter.repo_name|capitalize }}.from_file(test_config).app 44 | app.config['TESTING'] = True 45 | # Default port is 5000 46 | app.config['LIVESERVER_PORT'] = 8943 47 | self.app = app.test_client() 48 | return self.app 49 | 50 | def test_server_is_up(self): 51 | url = 'blueprint_example/test?q=${question}'.format( 52 | question="What's the meaning of life?" 53 | ) 54 | response = self.app.get(url) 55 | self.assertNotEqual(response.status_code, 404) 56 | 57 | def test_response(self): 58 | url = 'blueprint_example/test?q=${question}'.format( 59 | question="What's the meaning of life?" 60 | ) 61 | response = self.app.get(url) 62 | 63 | self.assertIsNotNone(response.data) 64 | data = json.loads(response.data) 65 | 66 | self.assertEqual( 67 | set(data.keys()), 68 | set(["answer", "this"]) 69 | ) 70 | 71 | 72 | def suite(): 73 | from .helpers import setup_path 74 | setup_path() 75 | suite = unittest.TestSuite() 76 | suite.addTest(unittest.makeSuite({{ cookiecutter.repo_name | capitalize }})) 77 | suite.addTest(unittest.makeSuite(BlueprintExample)) 78 | return suite 79 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/util.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 - *- 2 | """Utility and helper methods for {{ cookiecutter.project_name }}. 3 | 4 | {{ cookiecutter.project_name }}.util 5 | {{ '=' * cookiecutter.project_name|count }}~~~~~ 6 | 7 | """ 8 | 9 | from __future__ import absolute_import, division, print_function, \ 10 | with_statement, unicode_literals 11 | 12 | import sys 13 | import collections 14 | 15 | import pkgutil 16 | 17 | from ._compat import text_type, string_types, PY2, reraise 18 | 19 | # Code from https://github.com/pypa/warehouse 20 | # Copyright 2013 Donald Stufft 21 | # 22 | # Licensed under the Apache License, Version 2.0 (the "License"); 23 | # you may not use this file except in compliance with the License. 24 | # You may obtain a copy of the License at 25 | # 26 | # http://www.apache.org/licenses/LICENSE-2.0 27 | # 28 | # Unless required by applicable law or agreed to in writing, software 29 | # distributed under the License is distributed on an "AS IS" BASIS, 30 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 31 | # See the License for the specific language governing permissions and 32 | # limitations under the License. 33 | 34 | 35 | class AttributeDict(dict): 36 | 37 | def __getattr__(self, name): 38 | if not name in self: 39 | raise AttributeError("'{}' object has no attribute '{}'".format( 40 | self.__class__, 41 | name, 42 | )) 43 | 44 | return self[name] 45 | 46 | def __dir__(self): 47 | return list( 48 | self.__dict__.keys()) + [text_type(key) for key in self.keys() 49 | ] 50 | 51 | def merge_dict(base, additional): 52 | if base is None: 53 | return additional 54 | 55 | if additional is None: 56 | return base 57 | 58 | if not (isinstance(base, collections.Mapping) 59 | and isinstance(additional, collections.Mapping)): 60 | return additional 61 | 62 | merged = base 63 | for key, value in additional.items(): 64 | if isinstance(value, collections.Mapping): 65 | merged[key] = merge_dict(merged.get(key), value) 66 | else: 67 | merged[key] = value 68 | 69 | return merged 70 | 71 | 72 | def convert_to_attr_dict(dictionary): 73 | output = {} 74 | for key, value in dictionary.items(): 75 | if isinstance(value, collections.Mapping): 76 | output[key] = convert_to_attr_dict(value) 77 | else: 78 | output[key] = value 79 | return AttributeDict(output) 80 | 81 | # Code from https://github.com/mitsuhiko/werkzeug 82 | # Copyright 2013 Werkzeug Team. 83 | # LICENSE: https://github.com/mitsuhiko/werkzeug/blob/master/LICENSE 84 | 85 | 86 | class ImportStringError(ImportError): 87 | 88 | """Provides information about a failed :func:`import_string` attempt.""" 89 | 90 | #: String in dotted notation that failed to be imported. 91 | import_name = None 92 | #: Wrapped exception. 93 | exception = None 94 | 95 | def __init__(self, import_name, exception): 96 | self.import_name = import_name 97 | self.exception = exception 98 | 99 | msg = ( 100 | 'import_string() failed for %r. Possible reasons are:\n\n' 101 | '- missing __init__.py in a package;\n' 102 | '- package or module path not included in sys.path;\n' 103 | '- duplicated package or module name taking precedence in ' 104 | 'sys.path;\n' 105 | '- missing module, class, function or variable;\n\n' 106 | 'Debugged import:\n\n%s\n\n' 107 | 'Original exception:\n\n%s: %s') 108 | 109 | name = '' 110 | tracked = [] 111 | for part in import_name.replace(':', '.').split('.'): 112 | name += (name and '.') + part 113 | imported = import_string(name, silent=True) 114 | if imported: 115 | tracked.append((name, getattr(imported, '__file__', None))) 116 | else: 117 | track = ['- %r found in %r.' % (n, i) for n, i in tracked] 118 | track.append('- %r not found.' % name) 119 | msg = msg % (import_name, '\n'.join(track), 120 | exception.__class__.__name__, str(exception)) 121 | break 122 | 123 | ImportError.__init__(self, msg) 124 | 125 | def __repr__(self): 126 | return '<%s(%r, %r)>' % (self.__class__.__name__, self.import_name, 127 | self.exception) 128 | 129 | 130 | def import_string(import_name, silent=False): 131 | """Imports an object based on a string. This is useful if you want to 132 | use import paths as endpoints or something similar. An import path can 133 | be specified either in dotted notation (``xml.sax.saxutils.escape``) 134 | or with a colon as object delimiter (``xml.sax.saxutils:escape``). 135 | 136 | If `silent` is True the return value will be `None` if the import fails. 137 | 138 | :param import_name: the dotted name for the object to import. 139 | :param silent: if set to `True` import errors are ignored and 140 | `None` is returned instead. 141 | :return: imported object 142 | """ 143 | #XXX: py3 review needed 144 | assert isinstance(import_name, string_types) 145 | # force the import name to automatically convert to strings 146 | import_name = str(import_name) 147 | try: 148 | if ':' in import_name: 149 | module, obj = import_name.split(':', 1) 150 | elif '.' in import_name: 151 | module, obj = import_name.rsplit('.', 1) 152 | else: 153 | return __import__(import_name) 154 | # __import__ is not able to handle unicode strings in the fromlist 155 | # if the module is a package 156 | if PY2 and isinstance(obj, unicode): 157 | obj = obj.encode('utf-8') 158 | try: 159 | return getattr(__import__(module, None, None, [obj]), obj) 160 | except (ImportError, AttributeError): 161 | # support importing modules not yet set up by the parent module 162 | # (or package for that matter) 163 | modname = module + '.' + obj 164 | __import__(modname) 165 | return sys.modules[modname] 166 | except ImportError as e: 167 | if not silent: 168 | reraise( 169 | ImportStringError, 170 | ImportStringError(import_name, e), 171 | sys.exc_info()[2]) 172 | 173 | 174 | def find_modules(import_path, include_packages=False, recursive=False): 175 | """Find all the modules below a package. This can be useful to 176 | automatically import all views / controllers so that their metaclasses / 177 | function decorators have a chance to register themselves on the 178 | application. 179 | 180 | Packages are not returned unless `include_packages` is `True`. This can 181 | also recursively list modules but in that case it will import all the 182 | packages to get the correct load path of that module. 183 | 184 | :param import_name: the dotted name for the package to find child modules. 185 | :param include_packages: set to `True` if packages should be returned, too. 186 | :param recursive: set to `True` if recursion should happen. 187 | :return: generator 188 | """ 189 | module = import_string(import_path) 190 | path = getattr(module, '__path__', None) 191 | if path is None: 192 | raise ValueError('%r is not a package' % import_path) 193 | basename = module.__name__ + '.' 194 | for importer, modname, ispkg in pkgutil.iter_modules(path): 195 | modname = basename + modname 196 | if ispkg: 197 | if include_packages: 198 | yield modname 199 | if recursive: 200 | for item in find_modules(modname, include_packages, True): 201 | yield item 202 | else: 203 | yield modname 204 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import ( 5 | absolute_import, division, print_function, with_statement, 6 | unicode_literals 7 | ) 8 | 9 | 10 | import sys 11 | import os 12 | import argparse 13 | 14 | import kaptan 15 | from flask import Flask 16 | 17 | from . import exc 18 | from ._compat import reraise 19 | from .settings import default_config 20 | from .util import convert_to_attr_dict, merge_dict, import_string 21 | 22 | 23 | class {{ cookiecutter.repo_name | capitalize }}(object): 24 | """App factory.""" 25 | 26 | def __init__(self, config): 27 | 28 | #: configuration dictionary. Available as attributes. ``.config.debug`` 29 | self.config = convert_to_attr_dict(config) 30 | 31 | app = Flask(__name__) 32 | app.config.from_object(dict(self.config)) 33 | 34 | # iterate through blueprints in config. 35 | for url_prefix, blueprint_str in self.config.blueprints.items(): 36 | blueprint = import_string(blueprint_str) 37 | 38 | try: 39 | app.register_blueprint(blueprint, url_prefix=url_prefix) 40 | except AttributeError as e: 41 | type, value, traceback = sys.exc_info() 42 | raise reraise(exc.AppBlueprintLoadException, value, traceback) 43 | 44 | self.app = app 45 | 46 | def __call__(self): 47 | return self.app 48 | 49 | def run(self, host=None, port=None, debug=None, **options): 50 | """Pass-through to ``~.app`` object's :meth:`Flask.run()`. Mixes in 51 | ``port`` and ``host``.""" 52 | 53 | port = self.config.get('port') or port 54 | host = self.config.get('host') or host 55 | debug = self.config.get('debug') or debug 56 | app_config = {(k, v) for k, v in self.config.items() if k in Flask.default_config.keys()} 57 | options = merge_dict(app_config, options) 58 | 59 | self.app.run(host, port, debug, **options) 60 | 61 | @classmethod 62 | def from_file(cls, config_path=None, *args, **kwargs): 63 | """Create a Flask app instance from a JSON or YAML config. 64 | :param config_path: path to custom config file 65 | :type confiig_path: str 66 | :rtype: :class:`{{ cookiecutter.repo_name | capitalize }}` 67 | """ 68 | 69 | config = dict() 70 | configReader = kaptan.Kaptan() 71 | 72 | config = configReader.import_config(default_config).get() 73 | 74 | if config_path: 75 | if not os.path.exists(config_path): 76 | raise Exception('{0} does not exist.'.format(os.path.abspath(config_path))) 77 | if not any(config_path.endswith(ext) for ext in ('json', 'yml', 'yaml', 'ini')): 78 | raise Exception( 79 | '{0} does not have a yaml,yml,json,ini extension.' 80 | .format(os.path.abspath(config_path)) 81 | ) 82 | else: 83 | custom_config = configReader.import_config(config_path).get() 84 | config = merge_dict(config, custom_config) 85 | 86 | return cls(config) 87 | 88 | @classmethod 89 | def from_cli(cls, argv): 90 | """Flask application instance from :py:class:`argparse` / CLI args. 91 | :param argv: list of arguments, i.e. ``['-c', 'dev/config.yml']``. 92 | :type argv: list 93 | :rtype: :class:`{{ cookiecutter.repo_name | capitalize }}` with 94 | :class:`flask.Flask` on :prop:`self.app`. 95 | """ 96 | parser = argparse.ArgumentParser(prog="git_pull") 97 | parser.add_argument("-c", "--config", dest="_config") 98 | 99 | args = parser.parse_args(argv) 100 | config = args._config if args._config is not None else None 101 | 102 | return cls.from_file(config) 103 | --------------------------------------------------------------------------------