[Python-modules-commits] [cookiecutter] 01/06: Import cookiecutter_1.3.0.orig.tar.gz
Vincent Bernat
bernat at moszumanska.debian.org
Sat Dec 5 16:25:55 UTC 2015
This is an automated email from the git hooks/post-receive script.
bernat pushed a commit to branch master
in repository cookiecutter.
commit 1016a2016d22987f1c56f7208ef8693ab9101f96
Author: Vincent Bernat <bernat at debian.org>
Date: Sat Dec 5 17:09:27 2015 +0100
Import cookiecutter_1.3.0.orig.tar.gz
---
CONTRIBUTING.rst | 8 ++
HISTORY.rst | 41 +++++++++
Makefile | 18 +++-
README.rst | 16 ++++
appveyor.yml | 14 ++-
cookiecutter/__init__.py | 2 +-
cookiecutter/cli.py | 30 +++++-
cookiecutter/config.py | 39 +++++---
cookiecutter/exceptions.py | 6 ++
cookiecutter/generate.py | 25 +++--
cookiecutter/hooks.py | 15 ++-
cookiecutter/main.py | 8 +-
cookiecutter/vcs.py | 1 +
docs/advanced_usage.rst | 48 +++++++++-
docs/conf.py | 2 +-
docs/cookiecutter.rst | 8 --
docs/requirements.txt | 2 +
docs/types_of_contributions.rst | 8 ++
setup.cfg | 11 +++
setup.py | 19 ++--
tests/hooks-abort-render/hooks/post_gen_project.py | 12 +++
tests/hooks-abort-render/hooks/pre_gen_project.py | 12 +++
.../{{cookiecutter.repo_dir}}/README.rst | 2 +
tests/skipif_markers.py | 14 +--
tests/test_abort_generate_on_hook_error.py | 46 ++++++++++
tests/test_cli.py | 102 ++++++++++++++++++++-
tests/test_cookiecutters.py | 7 +-
tests/test_get_user_config.py | 53 +++++++++++
tests/test_hooks.py | 16 +++-
tests/test_more_cookiecutters.py | 7 +-
tests/test_vcs.py | 13 +++
31 files changed, 531 insertions(+), 74 deletions(-)
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 9493126..cf36c48 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -68,6 +68,14 @@ Cookiecutter could always use more documentation, whether as part of the
official Cookiecutter docs, in docstrings, or even on the web in blog posts,
articles, and such.
+If you want to review your changes on the documentation locally, you can do::
+
+ pip install -r docs/requirements.txt
+ make servedocs
+
+This will compile the documentation, open it in your browser and start
+watching the files for changes, recompiling as you save.
+
Submit Feedback
~~~~~~~~~~~~~~~
diff --git a/HISTORY.rst b/HISTORY.rst
index 25bfce5..96f7636 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,47 @@
History
-------
+1.3.0 (2015-11-10) Pumpkin Spice
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The goal of this release is to extend the user config feature and to make hook execution more robust.
+
+New Features:
+
+* Abort project generation if ``pre_gen_project`` or ``post_gen_project`` hook scripts fail, thanks to `@eliasdorneles`_ (#464, #549)
+* Extend user config capabilities with additional cli options ``--config-file``
+ and ``--default-config`` and environment variable ``COOKIECUTTER_CONFIG``, thanks to `@jhermann`_, `@pfmoore`_, and `@hackebrot`_ (#258, #424, #565)
+
+Bug Fixes:
+
+* Fixed conditional dependencies for wheels in setup.py, thanks to `@hackebrot`_ (#557, #568)
+* Reverted skipif markers to use correct reasons (bug fixed in pytest), thanks to `@hackebrot`_ (#574)
+
+
+Other Changes:
+
+* Improved path and documentation for rendering the Sphinx documentation, thanks to `@eliasdorneles`_ and `@hackebrot`_ (#562, #583)
+* Added additional help entrypoints, thanks to `@michaeljoseph`_ (#563, #492)
+* Added Two Scoops Academy to the README, thanks to `@hackebrot`_ (#576)
+* Now handling trailing slash on URL, thanks to `@ramiroluz`_ (#573, #546)
+* Support for testing x86 and x86-64 architectures on appveyor, thanks to `@maiksensi`_ (#567)
+* Made tests work without installing Cookiecutter, thanks to `@vincentbernat`_ (#550)
+* Encoded the result of the hook template to utf8, thanks to `@ionelmc`_ (#577. #578)
+* Added test for _run_hook_from_repo_dir, thanks to `@hackebrot`_ (#579, #580)
+* Implemented bumpversion, thanks to `@hackebrot`_ (#582)
+* Added more cookiecutter templates to the mix:
+
+ * `cookiecutter-octoprint-plugin`_ by `@foosel`_ (#560)
+ * `wagtail-cookiecutter-foundation`_ by `@chrisdev`_, et al. (#566)
+
+.. _`@foosel`: https://github.com/foosel
+.. _`@chrisdev`: https://github.com/chrisdev
+.. _`@jhermann`: https://github.com/jhermann
+
+.. _`cookiecutter-octoprint-plugin`: https://github.com/OctoPrint/cookiecutter-octoprint-plugin
+.. _`wagtail-cookiecutter-foundation`: https://github.com/chrisdev/wagtail-cookiecutter-foundation
+
+
1.2.1 (2015-10-18) Zimtsterne
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Makefile b/Makefile
index 9e0208b..d214493 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,15 @@
.PHONY: clean-pyc clean-build docs
+define BROWSER_PYSCRIPT
+import os, webbrowser, sys
+try:
+ from urllib import pathname2url
+except:
+ from urllib.request import pathname2url
+
+webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
+endef
+export BROWSER_PYSCRIPT
+BROWSER := python -c "$$BROWSER_PYSCRIPT"
help:
@echo "clean-build - remove build artifacts"
@@ -35,7 +46,7 @@ test-all:
coverage:
tox -e cov-report
- open htmlcov/index.html
+ $(BROWSER) htmlcov/index.html
docs:
rm -f docs/cookiecutter.rst
@@ -44,7 +55,10 @@ docs:
$(MAKE) -C docs clean
$(MAKE) -C docs html
make contributing
- open docs/_build/html/index.html
+ $(BROWSER) docs/_build/html/index.html
+
+servedocs: docs
+ watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .
release: clean
python setup.py sdist bdist_wheel upload
diff --git a/README.rst b/README.rst
index 7456660..c9ce169 100644
--- a/README.rst
+++ b/README.rst
@@ -171,6 +171,7 @@ Python
* `cookiecutter-tapioca`_: A Template for building `tapioca-wrapper`_ based web API wrappers (clients).
* `cookiecutter-sublime-text-3-plugin`_: Sublime Text 3 plugin template with custom settings, commands, key bindings and main menu.
* `cookiecutter-muffin`_: A Muffin template with Bootstrap 3, starter templates, and working user registration.
+* `cookiecutter-octoprint-plugin`_: A template for building plugins for `OctoPrint`_.
Python-Django
^^^^^^^^^^^^^
@@ -186,6 +187,7 @@ Python-Django
* `cookiecutter-django-paas`_: Django template ready to use in SAAS platforms like Heroku, OpenShift, etc..
* `cookiecutter-django-rest-framework`_: A template for creating reusable Django REST Framework packages.
* `cookiecutter-wagtail`_ : A cookiecutter template for `Wagtail`_ CMS based sites.
+* `wagtail-cookiecutter-foundation`_: A complete template for Wagtail CMS projects featuring Zurb Foundation 5, ansible provisioning and deployment , front-end dependency management with bower, modular apps to get your site up and running including photo_gallery, RSS feed etc.
C
~~
@@ -294,6 +296,9 @@ HTML
.. _`cookiecutter-muffin`: https://github.com/drgarcia1986/cookiecutter-muffin
.. _`cookiecutter-wagtail`: https://github.com/torchbox/cookiecutter-wagtail
.. _`Wagtail`: https://github.com/torchbox/wagtail
+.. _`cookiecutter-octoprint-plugin`: https://github.com/OctoPrint/cookiecutter-octoprint-plugin
+.. _`OctoPrint`: https://github.com/foosel/OctoPrint
+.. _`wagtail-cookiecutter-foundation`: https://github.com/chrisdev/wagtail-cookiecutter-foundation
Scala
~~~~~
@@ -413,6 +418,17 @@ Waiting for a response to an issue/question?
* Need a fix/feature/release/help urgently, and can't wait? `@audreyr`_ is
available for hire for consultation or custom development.
+Support This Project
+--------------------
+
+This project is maintained by volunteers. Support their efforts by spreading the word about:
+
+.. image:: https://s3.amazonaws.com/tsacademy/images/tsa-logo-250x60-transparent-01.png
+ :name: Two Scoops Academy
+ :align: center
+ :alt: Two Scoops Academy
+ :target: http://www.twoscoops.academy/
+
Code of Conduct
---------------
diff --git a/appveyor.yml b/appveyor.yml
index 71450da..eaad642 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -6,19 +6,31 @@ environment:
- PYTHON: "C:\\Python27"
TOX_ENV: "py27"
+ - PYTHON: "C:\\Python27-x64"
+ TOX_ENV: "py27"
+
- PYTHON: "C:\\Python33"
TOX_ENV: "py33"
+ - PYTHON: "C:\\Python33-x64"
+ TOX_ENV: "py33"
+
- PYTHON: "C:\\Python34"
TOX_ENV: "py34"
+ - PYTHON: "C:\\Python34-x64"
+ TOX_ENV: "py34"
+
- PYTHON: "C:\\Python35"
TOX_ENV: "py35"
+ - PYTHON: "C:\\Python35-x64"
+ TOX_ENV: "py35"
+
init:
- "%PYTHON%/python -V"
- - "%PYTHON%/python -c \"import struct;print( 8 * struct.calcsize(\'P\'))\""
+ - "%PYTHON%/python -c \"import struct;print(8 * struct.calcsize(\'P\'))\""
install:
- "%PYTHON%/Scripts/easy_install -U pip"
diff --git a/cookiecutter/__init__.py b/cookiecutter/__init__.py
index caf230e..53a354a 100755
--- a/cookiecutter/__init__.py
+++ b/cookiecutter/__init__.py
@@ -8,4 +8,4 @@ cookiecutter
Main package for Cookiecutter.
"""
-__version__ = '1.2.1'
+__version__ = '1.3.0'
diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py
index 6f53e24..8825a8f 100755
--- a/cookiecutter/cli.py
+++ b/cookiecutter/cli.py
@@ -15,9 +15,10 @@ import logging
import click
from cookiecutter import __version__
+from cookiecutter.config import USER_CONFIG_PATH
from cookiecutter.main import cookiecutter
from cookiecutter.exceptions import (
- OutputDirExistsException, InvalidModeException
+ OutputDirExistsException, InvalidModeException, FailedHookException
)
logger = logging.getLogger(__name__)
@@ -30,7 +31,7 @@ def version_msg():
return message.format(location, python_version)
- at click.command()
+ at click.command(context_settings=dict(help_option_names=[u'-h', u'--help']))
@click.version_option(__version__, u'-V', u'--version', message=version_msg())
@click.argument(u'template')
@click.option(
@@ -59,8 +60,16 @@ def version_msg():
u'-o', u'--output-dir', default='.', type=click.Path(),
help=u'Where to output the generated project dir into'
)
+ at click.option(
+ u'--config-file', type=click.Path(), default=USER_CONFIG_PATH,
+ help=u'User configuration file'
+)
+ at click.option(
+ u'--default-config', is_flag=True,
+ help=u'Do not load a config file. Use the defaults instead'
+)
def main(template, no_input, checkout, verbose, replay, overwrite_if_exists,
- output_dir):
+ output_dir, config_file, default_config):
"""Create a project from a Cookiecutter project template (TEMPLATE)."""
if verbose:
logging.basicConfig(
@@ -75,15 +84,26 @@ def main(template, no_input, checkout, verbose, replay, overwrite_if_exists,
)
try:
+ # If you _need_ to support a local template in a directory
+ # called 'help', use a qualified path to the directory.
+ if template == u'help':
+ click.echo(click.get_current_context().get_help())
+ sys.exit(0)
+
+ user_config = None if default_config else config_file
+
cookiecutter(
template, checkout, no_input,
replay=replay,
overwrite_if_exists=overwrite_if_exists,
- output_dir=output_dir
+ output_dir=output_dir,
+ config_file=user_config
)
- except (OutputDirExistsException, InvalidModeException) as e:
+ except (OutputDirExistsException,
+ InvalidModeException, FailedHookException) as e:
click.echo(e)
sys.exit(1)
+
if __name__ == "__main__":
main()
diff --git a/cookiecutter/config.py b/cookiecutter/config.py
index 00c8113..edfcec4 100755
--- a/cookiecutter/config.py
+++ b/cookiecutter/config.py
@@ -25,6 +25,8 @@ from .exceptions import InvalidConfiguration
logger = logging.getLogger(__name__)
+USER_CONFIG_PATH = os.path.expanduser('~/.cookiecutterrc')
+
DEFAULT_CONFIG = {
'cookiecutters_dir': os.path.expanduser('~/.cookiecutters/'),
'replay_dir': os.path.expanduser('~/.cookiecutter_replay/'),
@@ -57,15 +59,30 @@ def get_config(config_path):
return config_dict
-def get_user_config():
- """
- Retrieve config from the user's ~/.cookiecutterrc, if it exists.
- Otherwise, return None.
+def get_user_config(config_file=USER_CONFIG_PATH):
+ """Retrieve the config from a file or return the defaults if None is
+ passed. If an environment variable `COOKIECUTTER_CONFIG` is set up, try
+ to load its value. Otherwise fall back to a default file or config.
"""
-
- # TODO: test on windows...
- USER_CONFIG_PATH = os.path.expanduser('~/.cookiecutterrc')
-
- if os.path.exists(USER_CONFIG_PATH):
- return get_config(USER_CONFIG_PATH)
- return copy.copy(DEFAULT_CONFIG)
+ # Do NOT load a config. Return defaults instead.
+ if config_file is None:
+ return copy.copy(DEFAULT_CONFIG)
+
+ # Load the given config file
+ if config_file and config_file is not USER_CONFIG_PATH:
+ return get_config(config_file)
+
+ try:
+ # Does the user set up a config environment variable?
+ env_config_file = os.environ['COOKIECUTTER_CONFIG']
+ except KeyError:
+ # Load an optional user config if it exists
+ # otherwise return the defaults
+ if os.path.exists(USER_CONFIG_PATH):
+ return get_config(USER_CONFIG_PATH)
+ else:
+ return copy.copy(DEFAULT_CONFIG)
+ else:
+ # There is a config environment variable. Try to load it.
+ # Do not check for existence, so invalid file paths raise an error.
+ return get_config(env_config_file)
diff --git a/cookiecutter/exceptions.py b/cookiecutter/exceptions.py
index 0ad6b58..415f99e 100755
--- a/cookiecutter/exceptions.py
+++ b/cookiecutter/exceptions.py
@@ -81,3 +81,9 @@ class InvalidModeException(CookiecutterException):
Raised when cookiecutter is called with both `no_input==True` and
`replay==True` at the same time.
"""
+
+
+class FailedHookException(CookiecutterException):
+ """
+ Raised when a hook script fails
+ """
diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py
index 94e6e94..7a4d461 100755
--- a/cookiecutter/generate.py
+++ b/cookiecutter/generate.py
@@ -24,10 +24,11 @@ from binaryornot.check import is_binary
from .exceptions import (
NonTemplatedInputDirException,
ContextDecodingException,
+ FailedHookException,
OutputDirExistsException
)
from .find import find_template
-from .utils import make_sure_path_exists, work_in
+from .utils import make_sure_path_exists, work_in, rmtree
from .hooks import run_hook
@@ -222,6 +223,20 @@ def ensure_dir_is_templated(dirname):
raise NonTemplatedInputDirException
+def _run_hook_from_repo_dir(repo_dir, hook_name, project_dir, context):
+ """
+ Run hook from repo directory, cleaning up project directory if hook fails
+ """
+ with work_in(repo_dir):
+ try:
+ run_hook(hook_name, project_dir, context)
+ except FailedHookException:
+ rmtree(project_dir)
+ logging.error("Stopping generation because %s"
+ " hook script didn't exit sucessfully" % hook_name)
+ raise
+
+
def generate_files(repo_dir, context=None, output_dir='.',
overwrite_if_exists=False):
"""
@@ -255,9 +270,7 @@ def generate_files(repo_dir, context=None, output_dir='.',
project_dir = os.path.abspath(project_dir)
logging.debug('project_dir is {0}'.format(project_dir))
- # run pre-gen hook from repo_dir
- with work_in(repo_dir):
- run_hook('pre_gen_project', project_dir, context)
+ _run_hook_from_repo_dir(repo_dir, 'pre_gen_project', project_dir, context)
with work_in(template_dir):
env = Environment(keep_trailing_newline=True)
@@ -313,8 +326,6 @@ def generate_files(repo_dir, context=None, output_dir='.',
logging.debug('f is {0}'.format(f))
generate_file(project_dir, infile, context, env)
- # run post-gen hook from repo_dir
- with work_in(repo_dir):
- run_hook('post_gen_project', project_dir, context)
+ _run_hook_from_repo_dir(repo_dir, 'post_gen_project', project_dir, context)
return project_dir
diff --git a/cookiecutter/hooks.py b/cookiecutter/hooks.py
index 37d1764..254bc2e 100755
--- a/cookiecutter/hooks.py
+++ b/cookiecutter/hooks.py
@@ -18,12 +18,15 @@ import tempfile
from jinja2 import Template
from cookiecutter import utils
+from .exceptions import FailedHookException
+
_HOOKS = [
'pre_gen_project',
'post_gen_project',
# TODO: other hooks should be listed here
]
+EXIT_SUCCESS = 0
def find_hooks():
@@ -67,7 +70,10 @@ def run_script(script_path, cwd='.'):
shell=run_thru_shell,
cwd=cwd
)
- proc.wait()
+ exit_status = proc.wait()
+ if exit_status != EXIT_SUCCESS:
+ raise FailedHookException(
+ "Hook script failed (exit status: %d)" % exit_status)
def run_script_with_context(script_path, cwd, context):
@@ -84,10 +90,11 @@ def run_script_with_context(script_path, cwd, context):
with tempfile.NamedTemporaryFile(
delete=False,
- mode='w',
+ mode='wb',
suffix=extension
) as temp:
- temp.write(Template(contents).render(**context))
+ output = Template(contents).render(**context)
+ temp.write(output.encode('utf-8'))
run_script(temp.name, cwd)
@@ -104,4 +111,4 @@ def run_hook(hook_name, project_dir, context):
if script is None:
logging.debug('No hooks found')
return
- return run_script_with_context(script, project_dir, context)
+ run_script_with_context(script, project_dir, context)
diff --git a/cookiecutter/main.py b/cookiecutter/main.py
index eae455b..e6d858f 100755
--- a/cookiecutter/main.py
+++ b/cookiecutter/main.py
@@ -16,7 +16,7 @@ import logging
import os
import re
-from .config import get_user_config
+from .config import get_user_config, USER_CONFIG_PATH
from .exceptions import InvalidModeException
from .prompt import prompt_for_config
from .generate import generate_context, generate_files
@@ -71,7 +71,8 @@ def expand_abbreviations(template, config_dict):
def cookiecutter(
template, checkout=None, no_input=False, extra_context=None,
- replay=False, overwrite_if_exists=False, output_dir='.'):
+ replay=False, overwrite_if_exists=False, output_dir='.',
+ config_file=USER_CONFIG_PATH):
"""
API equivalent to using Cookiecutter at the command line.
@@ -84,6 +85,7 @@ def cookiecutter(
:param: overwrite_if_exists: Overwrite the contents of output directory
if it exists
:param output_dir: Where to output the generated project dir into.
+ :param config_file: User configuration file path.
"""
if replay and ((no_input is not False) or (extra_context is not None)):
err_msg = (
@@ -94,7 +96,7 @@ def cookiecutter(
# Get user config from ~/.cookiecutterrc or equivalent
# If no config file, sensible defaults from config.DEFAULT_CONFIG are used
- config_dict = get_user_config()
+ config_dict = get_user_config(config_file=config_file)
template = expand_abbreviations(template, config_dict)
diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py
index 269fee3..4f7dbd9 100755
--- a/cookiecutter/vcs.py
+++ b/cookiecutter/vcs.py
@@ -103,6 +103,7 @@ def clone(repo_url, checkout=None, clone_to_dir=".", no_input=False):
msg = "'{0}' is not installed.".format(repo_type)
raise VCSNotInstalled(msg)
+ repo_url = repo_url.rstrip('/')
tail = os.path.split(repo_url)[1]
if repo_type == 'git':
repo_dir = os.path.normpath(os.path.join(clone_to_dir,
diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst
index 2809db7..5d762de 100644
--- a/docs/advanced_usage.rst
+++ b/docs/advanced_usage.rst
@@ -34,11 +34,55 @@ hooks, as these can be run on any platform. However, if you intend for your
template to only be run on a single platform, a shell script (or `.bat` file
on Windows) can be a quicker alternative.
+.. note::
+ Make sure your hook scripts work in a robust manner. If a hook script fails
+ (that is, `if it finishes with a nonzero exit status
+ <https://docs.python.org/3/library/sys.html#sys.exit>`_), the project
+ generation will stop and the generated directory will be cleaned up.
+
+Example: Validating template variables
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is an example of script that validates a template variable
+before generating the project, to be used as ``hooks/pre_gen_project.py``:
+
+.. code-block:: python
+
+ import re
+ import sys
+
+
+ MODULE_REGEX = r'^[_a-zA-Z][_a-zA-Z0-9]+$'
+
+ module_name = '{{ cookiecutter.module_name }}'
+
+ if not re.match(MODULE_REGEX, module_name):
+ print('ERROR: %s is not a valid Python module name!' % module_name)
+
+ # exits with status 1 to indicate failure
+ sys.exit(1)
+
+
User Config (0.7.0+)
----------------------
-If you use Cookiecutter a lot, you'll find it useful to have a
-`.cookiecutterrc` file in your home directory like this:
+If you use Cookiecutter a lot, you'll find it useful to have a user config
+file. By default Cookiecutter tries to retrieve settings from a `.cookiecutterrc`
+file in your home directory.
+
+From version 1.3.0 you can also specify a config file on the command line via ``--config-file``::
+
+ $ cookiecutter --config-file /home/audreyr/my-custom-config.yaml cookiecutter-pypackage
+
+Or you can set the ``COOKIECUTTER_CONFIG`` environment variable::
+
+ $ export COOKIECUTTER_CONFIG=/home/audreyr/my-custom-config.yaml
+
+If you wish to stick to the built-in config and not load any user config file at all,
+use the cli option ``--default-config`` instead. Preventing Cookiecutter from loading
+user settings is crucial for writing integration tests in an isolated environment.
+
+Example user config:
.. code-block:: yaml
diff --git a/docs/conf.py b/docs/conf.py
index e09f31b..ae5b80e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -132,7 +132,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
+html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
diff --git a/docs/cookiecutter.rst b/docs/cookiecutter.rst
index 0ff329a..7c91eff 100644
--- a/docs/cookiecutter.rst
+++ b/docs/cookiecutter.rst
@@ -12,14 +12,6 @@ cookiecutter.cli module
:undoc-members:
:show-inheritance:
-cookiecutter.compat module
---------------------------
-
-.. automodule:: cookiecutter.compat
- :members:
- :undoc-members:
- :show-inheritance:
-
cookiecutter.config module
--------------------------
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..fc1a115
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,2 @@
+watchdog==0.8.3
+sphinx-rtd-theme==0.1.9
diff --git a/docs/types_of_contributions.rst b/docs/types_of_contributions.rst
index ebff7ff..0230520 100644
--- a/docs/types_of_contributions.rst
+++ b/docs/types_of_contributions.rst
@@ -51,6 +51,14 @@ Cookiecutter could always use more documentation, whether as part of the
official Cookiecutter docs, in docstrings, or even on the web in blog posts,
articles, and such.
+If you want to review your changes on the documentation locally, you can do::
+
+ pip install -r docs/requirements.txt
+ make servedocs
+
+This will compile the documentation, open it in your browser and start
+watching the files for changes, recompiling as you save.
+
Submit Feedback
~~~~~~~~~~~~~~~
diff --git a/setup.cfg b/setup.cfg
index f5f139e..6ab4c54 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,16 @@
+[bumpversion]
+current_version = 1.3.0
+commit = True
+tag = True
+tag_name = {new_version}
+
+[bumpversion:file:setup.py]
+
+[bumpversion:file:cookiecutter/__init__.py]
+
[flake8]
ignore = E731
[bdist_wheel]
universal = 1
+
diff --git a/setup.py b/setup.py
index eff59cf..e6e63b9 100755
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,6 @@
#!/usr/bin/env python
import os
-import platform
import sys
try:
@@ -9,7 +8,7 @@ try:
except ImportError:
from distutils.core import setup
-version = "1.2.1"
+version = "1.3.0"
if sys.argv[-1] == 'publish':
os.system('python setup.py sdist upload')
@@ -35,14 +34,6 @@ requirements = [
'whichcraft>=0.1.1'
]
-# Use PyYAML for 2.7 on Windows, ruamel.yaml everywhere else
-PY2 = sys.version_info[0] == 2
-windows = platform.system().lower().startswith('windows')
-if PY2 and windows:
- requirements.append('PyYAML>=3.10')
-else:
- requirements.append('ruamel.yaml>=0.10.12')
-
long_description = readme + '\n\n' + history
if sys.argv[-1] == 'readme':
@@ -71,6 +62,14 @@ setup(
},
include_package_data=True,
install_requires=requirements,
+ extras_require={
+ ':sys_platform=="win32" and python_version=="2.7"': [
+ 'PyYAML>=3.10'
+ ],
+ ':sys_platform!="win32" or python_version!="2.7"': [
+ 'ruamel.yaml>=0.10.12'
+ ]
+ },
license='BSD',
zip_safe=False,
classifiers=[
diff --git a/tests/hooks-abort-render/hooks/post_gen_project.py b/tests/hooks-abort-render/hooks/post_gen_project.py
new file mode 100644
index 0000000..3816f29
--- /dev/null
+++ b/tests/hooks-abort-render/hooks/post_gen_project.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# flake8: noqa
+
+import sys
+
+{% if cookiecutter.abort_post_gen == "yes" %}
+sys.exit(1)
+{% else %}
+sys.exit(0)
+{% endif %}
diff --git a/tests/hooks-abort-render/hooks/pre_gen_project.py b/tests/hooks-abort-render/hooks/pre_gen_project.py
new file mode 100644
index 0000000..62da436
--- /dev/null
+++ b/tests/hooks-abort-render/hooks/pre_gen_project.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# flake8: noqa
+
+import sys
+
+{% if cookiecutter.abort_pre_gen == "yes" %}
+sys.exit(1)
+{% else %}
+sys.exit(0)
+{% endif %}
diff --git a/tests/hooks-abort-render/{{cookiecutter.repo_dir}}/README.rst b/tests/hooks-abort-render/{{cookiecutter.repo_dir}}/README.rst
new file mode 100644
index 0000000..6dfb07b
--- /dev/null
+++ b/tests/hooks-abort-render/{{cookiecutter.repo_dir}}/README.rst
@@ -0,0 +1,2 @@
+{{cookiecutter.repo_name}}
+==========================
diff --git a/tests/skipif_markers.py b/tests/skipif_markers.py
index 7b212fb..72b50d7 100644
--- a/tests/skipif_markers.py
+++ b/tests/skipif_markers.py
@@ -26,14 +26,10 @@ except KeyError:
else:
no_network = True
-# For some reason pytest incorrectly uses the first reason text regardless of
-# which condition matches. Using a unified message for now
-# travis_reason = 'Works locally with tox but fails on Travis.'
-# no_network_reason = 'Needs a network connection to GitHub.'
-reason = (
- 'Fails on Travis or else there is no network connection to '
- 'GitHub/Bitbucket.'
+skipif_travis = pytest.mark.skipif(
+ travis, reason='Works locally with tox but fails on Travis.'
)
-skipif_travis = pytest.mark.skipif(travis, reason=reason)
-skipif_no_network = pytest.mark.skipif(no_network, reason=reason)
+skipif_no_network = pytest.mark.skipif(
+ no_network, reason='Needs a network connection to GitHub/Bitbucket.'
+)
diff --git a/tests/test_abort_generate_on_hook_error.py b/tests/test_abort_generate_on_hook_error.py
new file mode 100644
index 0000000..34e5420
--- /dev/null
+++ b/tests/test_abort_generate_on_hook_error.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+
+import pytest
+
+from cookiecutter import generate
+from cookiecutter import exceptions
+
+
+ at pytest.mark.usefixtures('clean_system')
+def test_pre_gen_hook(tmpdir):
+ context = {
+ 'cookiecutter': {
+ "repo_dir": "foobar",
+ "abort_pre_gen": "yes",
+ "abort_post_gen": "no"
+ }
+ }
+
+ with pytest.raises(exceptions.FailedHookException):
+ generate.generate_files(
+ repo_dir='tests/hooks-abort-render',
+ context=context,
+ output_dir=str(tmpdir)
+ )
+
+ assert not tmpdir.join('foobar').isdir()
+
+
+ at pytest.mark.usefixtures('clean_system')
+def test_post_gen_hook(tmpdir):
+ context = {
+ 'cookiecutter': {
+ "repo_dir": "foobar",
+ "abort_pre_gen": "no",
+ "abort_post_gen": "yes"
+ }
+ }
+
+ with pytest.raises(exceptions.FailedHookException):
+ generate.generate_files(
+ repo_dir='tests/hooks-abort-render',
+ context=context,
+ output_dir=str(tmpdir)
+ )
+
+ assert not tmpdir.join('foobar').isdir()
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 60f8795..383d7ea 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -5,7 +5,7 @@ from click.testing import CliRunner
from cookiecutter.cli import main
from cookiecutter.main import cookiecutter
-from cookiecutter import utils
+from cookiecutter import utils, config
runner = CliRunner()
@@ -80,7 +80,8 @@ def test_cli_replay(mocker):
False,
replay=True,
overwrite_if_exists=False,
- output_dir='.'
+ output_dir='.',
+ config_file=config.USER_CONFIG_PATH
)
@@ -114,7 +115,8 @@ def test_cli_exit_on_noinput_and_replay(mocker):
True,
replay=True,
overwrite_if_exists=False,
- output_dir='.'
+ output_dir='.',
+ config_file=config.USER_CONFIG_PATH
)
@@ -147,7 +149,8 @@ def test_run_cookiecutter_on_overwrite_if_exists_and_replay(
False,
replay=True,
overwrite_if_exists=True,
- output_dir='.'
+ output_dir='.',
+ config_file=config.USER_CONFIG_PATH
)
@@ -201,5 +204,94 @@ def test_cli_output_dir(mocker, output_dir_flag, output_dir):
False,
replay=False,
overwrite_if_exists=False,
- output_dir=output_dir
+ output_dir=output_dir,
+ config_file=config.USER_CONFIG_PATH
+ )
+
+
+ at pytest.fixture(params=['-h', '--help', 'help'])
+def help_cli_flag(request):
+ return request.param
+
+
+def test_cli_help(help_cli_flag):
+ result = runner.invoke(main, [help_cli_flag])
+ assert result.exit_code == 0
+ assert result.output.startswith('Usage')
+
+
+ at pytest.fixture
+def user_config_path(tmpdir):
+ return str(tmpdir.join('tests/config.yaml'))
+
+
+def test_user_config(mocker, user_config_path):
+ mock_cookiecutter = mocker.patch(
+ 'cookiecutter.cli.cookiecutter'
+ )
+
+ template_path = 'tests/fake-repo-pre/'
+ result = runner.invoke(main, [
+ template_path,
+ '--config-file',
+ user_config_path
+ ])
+
+ assert result.exit_code == 0
+ mock_cookiecutter.assert_called_once_with(
+ template_path,
+ None,
+ False,
+ replay=False,
+ overwrite_if_exists=False,
+ output_dir='.',
+ config_file=user_config_path
+ )
+
+
+def test_default_user_config_overwrite(mocker, user_config_path):
+ mock_cookiecutter = mocker.patch(
+ 'cookiecutter.cli.cookiecutter'
+ )
+
+ template_path = 'tests/fake-repo-pre/'
+ result = runner.invoke(main, [
+ template_path,
+ '--config-file',
+ user_config_path,
+ '--default-config'
+ ])
+
+ assert result.exit_code == 0
+ mock_cookiecutter.assert_called_once_with(
+ template_path,
+ None,
+ False,
+ replay=False,
+ overwrite_if_exists=False,
+ output_dir='.',
+ config_file=None
+ )
+
+
+def test_default_user_config(mocker):
+ mock_cookiecutter = mocker.patch(
+ 'cookiecutter.cli.cookiecutter'
+ )
+
+ template_path = 'tests/fake-repo-pre/'
+ result = runner.invoke(main, [
+ template_path,
+ '--default-config'
+ ])
+
+ assert result.exit_code == 0
+ mock_cookiecutter.assert_called_once_with(
+ template_path,
+ None,
+ False,
+ replay=False,
+ overwrite_if_exists=False,
+ output_dir='.',
... 182 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/cookiecutter.git
More information about the Python-modules-commits
mailing list