pax_global_header00006660000000000000000000000064126204720040014507gustar00rootroot0000000000000052 comment=69e0fb66274ef7e92bcdc4c1f5010b984ac3e2f2 cookiecutter-1.3.0/000077500000000000000000000000001262047200400142105ustar00rootroot00000000000000cookiecutter-1.3.0/.gitignore000066400000000000000000000006171262047200400162040ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox .cache nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # Sphinx docs/_build # Coverage htmlcov/ # TextMate .tm_properties # Local env .envs cookiecutter-1.3.0/.travis.yml000066400000000000000000000006531262047200400163250ustar00rootroot00000000000000# Config file for automatic testing at travis-ci.org sudo: false language: python python: 3.5 env: - TOX_ENV=py27 - TOX_ENV=py33 - TOX_ENV=py34 - TOX_ENV=py35 - TOX_ENV=pypy - TOX_ENV=flake8 script: tox -e $TOX_ENV install: - pip install tox after_success: # Report coverage results to codecov.io # and export tox environment variables - pip install codecov - codecov -e TOX_ENV cookiecutter-1.3.0/AUTHORS.rst000066400000000000000000000116541262047200400160760ustar00rootroot00000000000000======= Credits ======= Development Leads ----------------- * Audrey Roy Greenfeld (`@audreyr`_) * Daniel Roy Greenfeld (`@pydanny`_) Core Committers --------------- * Michael Joseph (`@michaeljoseph`_) * Paul Moore (`@pfmoore`_) * Raphael Pierzina (`@hackebrot`_) Contributors ------------ * Steven Loria (`@sloria`_) * Goran Peretin (`@gperetin`_) * Hamish Downer (`@foobacca`_) * Thomas Orozco (`@krallin`_) * Jindrich Smitka (`@s-m-i-t-a`_) * Benjamin Schwarze (`@benjixx`_) * Raphi (`@raphigaziano`_) * Thomas Chiroux (`@ThomasChiroux`_) * Sergi Almacellas Abellana (`@pokoli`_) * Alex Gaynor (`@alex`_) * Rolo (`@rolo`_) * Pablo (`@oubiga`_) * Bruno Rocha (`@rochacbruno`_) * Alexander Artemenko (`@svetlyak40wt`_) * Mahmoud Abdelkader (`@mahmoudimus`_) * Leonardo Borges Avelino (`@lborgav`_) * Chris Trotman (`@solarnz`_) * Rolf (`@relekang`_) * Noah Kantrowitz (`@coderanger`_) * Vincent Bernat (`@vincentbernat`_) * Germán Moya (`@pbacterio`_) * Ned Batchelder (`@nedbat`_) * Dave Dash (`@davedash`_) * Johan Charpentier (`@cyberj`_) * Éric Araujo (`@merwok`_) * saxix (`@saxix`_) * Tzu-ping Chung (`@uranusjr`_) * Caleb Hattingh (`@cjrh`_) * Flavio Curella (`@fcurella`_) * Adam Venturella (`@aventurella`_) * Monty Taylor (`@emonty`_) * schacki (`@schacki`_) * Ryan Olson (`@ryanolson`_) * Trey Hunner (`@treyhunner`_) * Russell Keith-Magee (`@freakboy3742`_) * Mishbah Razzaque (`@mishbahr`_) * Robin Andeer (`@robinandeer`_) * Rachel Sanders (`@trustrachel`_) * Rémy Hubscher (`@Natim`_) * Dino Petron3 (`@dinopetrone`_) * Peter Inglesby (`@inglesp`_) * Ramiro Batista da Luz (`@ramiroluz`_) * Omer Katz (`@thedrow`_) * lord63 (`@lord63`_) * Randy Syring (`@rsyring`_) * Mark Jones (`@mark0978`_) * Marc Abramowitz (`@msabramo`_) * Lucian Ursu (`@LucianU`_) * Osvaldo Santana Neto (`@osantana`_) * Matthias84 (`@Matthias84`_) * Simeon Visser (`@svisser`_) * Guruprasad (`@lgp171188`_) * Charles-Axel Dein (`@charlax`_) * Diego Garcia (`@drgarcia1986`_) * maiksensi (`@maiksensi`_) * Andrew Conti (`@agconti`_) * Valentin Lab (`@vaab`_) * Ilja Bauer (`@iljabauer`_) * Elias Dorneles (`@eliasdorneles`_) .. _`@maiksensi`: https://github.com/maiksensi .. _`@svisser`: https://github.com/svisser .. _`@LucianU`: https://github.com/LucianU .. _`@osantana`: https://github.com/osantana .. _`@msabramo`: https://github.com/msabramo .. _`@mark0978`: https://github.com/mark0978 .. _`@rsyring`: https://github.com/rsyring .. _`@vincentbernat`: https://github.com/vincentbernat .. _`@audreyr`: https://github.com/audreyr .. _`@pydanny`: https://github.com/pydanny .. _`@sloria`: https://github.com/sloria .. _`@gperetin`: https://github.com/gperetin .. _`@foobacca`: https://github.com/foobacca .. _`@krallin`: https://github.com/krallin .. _`@s-m-i-t-a`: https://github.com/s-m-i-t-a .. _`@benjixx`: https://github.com/benjixx .. _`@raphigaziano`: https://github.com/raphigaziano .. _`@ThomasChiroux`: https://github.com/ThomasChiroux .. _`@pokoli`: https://github.com/pokoli .. _`@alex`: https://github.com/alex .. _`@rolo`: https://github.com/rolo .. _`@oubiga`: https://github.com/oubiga .. _`@michaeljoseph`: https://github.com/michaeljoseph .. _`@rochacbruno`: https://github.com/rochacbruno .. _`@svetlyak40wt`: https://github.com/svetlyak40wt .. _`@mahmoudimus`: https://github.com/mahmoudimus .. _`@lborgav`: https://github.com/lborgav .. _`@solarnz`: https://github.com/solarnz .. _`@relekang`: https://github.com/relekang .. _`@coderanger`: https://github.com/coderanger .. _`@pbacterio`: https://github.com/pbacterio .. _`@nedbat`: https://github.com/nedbat .. _`@davedash`: https://github.com/davedash .. _`@cyberj`: https://github.com/cyberj .. _`@merwok`: https://github.com/merwok .. _`@hackebrot`: https://github.com/hackebrot .. _`@saxix`: https://github.com/saxix .. _`@uranusjr`: https://github.com/uranusjr .. _`@cjrh`: https://github.com/cjrh .. _`@pfmoore`: https://github.com/pfmoore .. _`@fcurella`: https://github.com/fcurella .. _`@aventurella`: https://github.com/aventurella .. _`@emonty`: https://github.com/emonty .. _`@schacki`: https://github.com/schacki .. _`@ryanolson`: https://github.com/ryanolson .. _`@treyhunner`: https://github.com/treyhunner .. _`@freakboy3742`: https://github.com/freakboy3742 .. _`@mishbahr`: https://github.com/mishbahr .. _`@robinandeer`: https://github.com/robinandeer .. _`@trustrachel`: https://github.com/trustrachel .. _`@Natim`: https://github.com/Natim .. _`@dinopetrone`: https://github.com/dinopetrone .. _`@inglesp`: https://github.com/inglesp .. _`@ramiroluz`: https://github.com/ramiroluz .. _`@thedrow`: https://github.com/thedrow .. _`@lord63`: https://github.com/lord63 .. _`@Matthias84`: https://github.com/Matthias84 .. _`@lgp171188`: https://github.com/lgp171188 .. _`@charlax`: https://github.com/charlax .. _`@drgarcia1986`: https://github.com/drgarcia1986 .. _`@agconti`: https://github.com/agconti .. _`@vaab`: https://github.com/vaab .. _`@iljabauer`: https://github.com/iljabauer .. _`@eliasdorneles`: https://github.com/eliasdorneles cookiecutter-1.3.0/CONTRIBUTING.rst000066400000000000000000000352401262047200400166550ustar00rootroot00000000000000============ Contributing ============ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. .. toctree:: :numbered: :maxdepth: 2 types_of_contributions contributor_setup contributor_guidelines contributor_testing core_committer_guide Types of Contributions ---------------------- You can contribute in many ways: Create Cookiecutter Templates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some other Cookiecutter templates to list in the :ref:`README ` would be great. If you create a Cookiecutter template, submit a pull request adding it to README.rst. Report Bugs ~~~~~~~~~~~ Report bugs at https://github.com/audreyr/cookiecutter/issues. If you are reporting a bug, please include: * Your operating system name and version. * Any details about your local setup that might be helpful in troubleshooting. * If you can, provide detailed steps to reproduce the bug. * If you don't have steps to reproduce the bug, just note your observations in as much detail as you can. Questions to start a discussion about the issue are welcome. Fix Bugs ~~~~~~~~ Look through the GitHub issues for bugs. Anything tagged with "bug" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ Look through the GitHub issues for features. Anything tagged with "enhancement" is open to whoever wants to implement it. Please do not combine multiple feature enhancements into a single pull request. Note: this project is a bit conservative, so new features might not get into core. If possible, it's best to try and implement feature ideas as separate projects outside of the core codebase. Write Documentation ~~~~~~~~~~~~~~~~~~~ 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 ~~~~~~~~~~~~~~~ The best way to send feedback is to file an issue at https://github.com/audreyr/cookiecutter/issues. If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. * Remember that this is a volunteer-driven project, and that contributions are welcome :) Setting Up the Code for Local Development ----------------------------------------- Here's how to set up `cookiecutter` for local development. 1. Fork the `cookiecutter` repo on GitHub. 2. Clone your fork locally:: $ git clone git@github.com:your_name_here/cookiecutter.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: $ mkvirtualenv cookiecutter $ cd cookiecutter/ $ python setup.py develop 4. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. 5. When you're done making changes, check that your changes pass the tests and flake8:: $ pip install tox $ tox Please note that tox runs flake8 automatically, since we have a test environment for it. If you feel like running only the flake8 environment, please use the following command:: $ tox -e flake8 6. Commit your changes and push your branch to GitHub:: $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature 7. Check that the test coverage hasn't dropped:: $ tox -e cov-report 8. Submit a pull request through the GitHub website. Contributor Guidelines ----------------------- Pull Request Guidelines ~~~~~~~~~~~~~~~~~~~~~~~~ Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. 3. The pull request should work for Python 2.7, 3.3, 3.4, 3.5, and PyPy on Appveyor and Travis CI. 4. Check https://travis-ci.org/audreyr/cookiecutter/pull_requests and https://ci.appveyor.com/project/audreyr/cookiecutter/history to ensure the tests pass for all supported Python versions and platforms. Coding Standards ~~~~~~~~~~~~~~~~ * PEP8 * Functions over classes except in tests * Quotes via http://stackoverflow.com/a/56190/5549 * Use double quotes around strings that are used for interpolation or that are natural language messages * Use single quotes for small symbol-like strings (but break the rules if the strings contain quotes) * Use triple double quotes for docstrings and raw string literals for regular expressions even if they aren't needed. * Example: .. code-block:: python LIGHT_MESSAGES = { 'English': "There are %(number_of_lights)s lights.", 'Pirate': "Arr! Thar be %(number_of_lights)s lights." } def lights_message(language, number_of_lights): """Return a language-appropriate string reporting the light count.""" return LIGHT_MESSAGES[language] % locals() def is_pirate(message): """Return True if the given message sounds piratical.""" return re.search(r"(?i)(arr|avast|yohoho)!", message) is not None * Write new code in Python 3. Testing with tox ---------------- Tox uses py.test under the hood, hence it supports the same syntax for selecting tests. For further information please consult the `pytest usage docs`_. To run a particular test class with tox:: $ tox -e py '-k TestFindHooks' To run some tests with names matching a string expression:: $ tox -e py '-k generate' Will run all tests matching "generate", test_generate_files for example. To run just one method:: $ tox -e py '-k "TestFindHooks and test_find_hook"' To run all tests using various versions of python in virtualenvs defined in tox.ini, just run tox.:: $ tox This configuration file setup the pytest-cov plugin and it is an additional dependency. It generate a coverage report after the tests. It is possible to tests with some versions of python, to do this the command is:: $ tox -e py27,py34,pypy Will run py.test with the python2.7, python3.4 and pypy interpreters, for example. Troubleshooting for Contributors --------------------------------- Python 3.3 tests fail locally ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Try upgrading Tox to the latest version. I noticed that they were failing locally with Tox 1.5 but succeeding when I upgraded to Tox 1.7.1. .. _`pytest usage docs`: https://pytest.org/latest/usage.html#specifying-tests-selecting-tests Core Committer Guide ==================== Vision and Scope ----------------- Core committers, use this section to: * Guide your instinct and decisions as a core committer * Limit the codebase from growing infinitely Command-Line Accessible ~~~~~~~~~~~~~~~~~~~~~~~ * Provides a command-line utility that creates projects from cookiecutters * Extremely easy to use without having to think too hard * Flexible for more complex use via optional arguments API Accessible ~~~~~~~~~~~~~~ * Entirely function-based and stateless (Class-free by intentional design) * Usable in pieces for developers of template generation tools Being Jinja2-specific ~~~~~~~~~~~~~~~~~~~~~ * Sets a standard baseline for project template creators, facilitating reuse * Minimizes the learning curve for those who already use Flask or Django * Minimizes scope of Cookiecutter codebase Extensible ~~~~~~~~~~ Being extendable by people with different ideas for Jinja2-based project template tools. * Entirely function-based * Aim for statelessness * Lets anyone write more opinionated tools Freedom for Cookiecutter users to build and extend. * No officially-maintained cookiecutter templates, only ones by individuals * Commercial project-friendly licensing, allowing for private cookiecutters and private Cookiecutter-based tools Fast and Focused ~~~~~~~~~~~~~~~~ Cookiecutter is designed to do one thing, and do that one thing very well. * Cover the use cases that the core committers need, and as little as possible beyond that :) * Generates project templates from the command-line or API, nothing more * Minimize internal line of code (LOC) count * Ultra-fast project generation for high performance downstream tools Inclusive ~~~~~~~~~ * Cross-platform and cross-version support are more important than features/functionality * Fixing Windows bugs even if it's a pain, to allow for use by more beginner coders Stable ~~~~~~ * Aim for 100% test coverage and covering corner cases * No pull requests will be accepted that drop test coverage on any platform, including Windows * Conservative decisions patterned after CPython's conservative decisions with stability in mind * Stable APIs that tool builders can rely on * New features require a +1 from 3 core committers VCS-Hosted Templates ~~~~~~~~~~~~~~~~~~~~~ Cookiecutter project templates are intentionally hosted VCS repos as-is. * They are easily forkable * It's easy for users to browse forks and files * They are searchable via standard Github/Bitbucket/other search interface * Minimizes the need for packaging-related cruft files * Easy to create a public project template and host it for free * Easy to collaborate Process: Pull Requests ------------------------ If a pull request is untriaged: * Look at the roadmap * Set it for the milestone where it makes the most sense * Add it to the roadmap How to prioritize pull requests, from most to least important: #. Fixes for broken tests. Broken means broken on any supported platform or Python version. #. Extra tests to cover corner cases. #. Minor edits to docs. #. Bug fixes. #. Major edits to docs. #. Features. Ensure that each pull request meets all requirements in this checklist: https://gist.github.com/audreyr/4feef90445b9680475f2 Process: Issues ---------------- If an issue is a bug that needs an urgent fix, mark it for the next patch release. Then either fix it or mark as please-help. For other issues: encourage friendly discussion, moderate debate, offer your thoughts. New features require a +1 from 2 other core committers (besides yourself). Process: Roadmap ----------------- The roadmap is https://github.com/audreyr/cookiecutter/milestones?direction=desc&sort=due_date&state=open Due dates are flexible. Core committers can change them as needed. Note that GitHub sort on them is buggy. How to number milestones: * Follow semantic versioning. See http://semver.org Milestone size: * If a milestone contains too much, move some to the next milestone. * Err on the side of more frequent patch releases. Process: Pull Request merging and HISTORY.rst maintenance --------------------------------------------------------- If you merge a pull request, you're responsible for updating `AUTHORS.rst` and `HISTORY.rst` When you're processing the first change after a release, create boilerplate following the existing pattern: x.y.z (Development) ~~~~~~~~~~~~~~~~~~~ The goals of this release are TODO: release summary of features Features: * Feature description, thanks to @contributor (#PR). Bug Fixes: * Bug fix description, thanks to @contributor (#PR). Other changes: * Description of the change, thanks to @contributor (#PR). .. _`@contributor`: https://github.com/contributor Process: Accepting Template Pull Requests ----------------------------------------- #. Run the template to generate the project. #. Attempt to start/use the rendered project. #. Merge the template in. #. Update the history file. .. note:: Adding a template doesn't give authors credit. Process: Generating CONTRIBUTING.rst ------------------------------------- From the `cookiecutter` project root:: $ make contributing This will generate the following message:: rm CONTRIBUTING.rst touch CONTRIBUTING.rst cat docs/contributing.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/types_of_contributions.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_setup.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_guidelines.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_tips.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/core_committer_guide.rst >> CONTRIBUTING.rst echo "\r\rAutogenerated from the docs via \`make contributing\`" >> CONTRIBUTING.rst echo "WARNING: Don't forget to replace any :ref: statements with literal names" WARNING: Don't forget to replace any :ref: statements with literal names Process: Your own code changes ------------------------------- All code changes, regardless of who does them, need to be reviewed and merged by someone else. This rule applies to all the core committers. Exceptions: * Minor corrections and fixes to pull requests submitted by others. * While making a formal release, the release manager can make necessary, appropriate changes. * Small documentation changes that reinforce existing subject matter. Most commonly being, but not limited to spelling and grammar corrections. Responsibilities ----------------- #. Ensure cross-platform compatibility for every change that's accepted. Windows, Mac, Debian & Ubuntu Linux. #. Ensure that code that goes into core meets all requirements in this checklist: https://gist.github.com/audreyr/4feef90445b9680475f2 #. Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback. #. Don't add any classes to the codebase unless absolutely needed. Err on the side of using functions. #. Keep feature versions as small as possible, preferably one new feature per version. #. Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the Python Community Code of Conduct (https://www.python.org/psf/codeofconduct/). Becoming a Core Committer -------------------------- Contributors may be given core commit privileges. Preference will be given to those with: A. Past contributions to Cookiecutter and other open-source projects. Contributions to Cookiecutter include both code (both accepted and pending) and friendly participation in the issue tracker. Quantity and quality are considered. B. A coding style that the other core committers find simple, minimal, and clean. C. Access to resources for cross-platform development and testing. D. Time to devote to the project regularly. Autogenerated from the docs via `make contributing` cookiecutter-1.3.0/HISTORY.rst000066400000000000000000000633521262047200400161140ustar00rootroot00000000000000.. :changelog: 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *Zimtsterne are cinnamon star cookies* New Feature: * Returns rendered project dir, thanks to `@hackebrot`_ (#553) Bug Fixes: * Factor in *choice* variables (as introduced in 1.1.0) when using a user config or extra context, thanks to `@ionelmc`_ and `@hackebrot`_ (#536, #542). Other Changes: * Enable py35 support on Travis by using Python 3.5 as base Python (`@maiksensi`_ / #540) * If a filename is empty, do not generate. Log instead (`@iljabauer`_ / #444) * Fix tests as per last changes in `cookiecutter-pypackage`_, thanks to `@eliasdorneles`_ (#555). * Removed deprecated cookiecutter-pylibrary-minimal from the list, thanks to `@ionelmc`_ (#556) * Moved to using `rualmel.yaml` instead of `PyYAML`, except for Windows users on Python 2.7, thanks to `@pydanny`_ (#557) .. _`cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage .. _`@iljabauer`: https://github.com/iljabauer .. _`@eliasdorneles`: https://github.com/eliasdorneles *Why 1.2.1 instead of 1.2.0? There was a problem in the distribution that we pushed to PyPI. Since you can't replace previous files uploaded to PyPI, we deleted the files on PyPI and released 1.2.1.* 1.1.0 (2015-09-26) Snickerdoodle ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The goals of this release were `copy without render` and a few additional command-line options such as `--overwrite-if-exists`, `—replay`, and `output-dir`. Features: * Added `copy without render`_ feature, making it much easier for developers of Ansible, Salt Stack, and other recipe-based tools to work with Cookiecutter. Thanks to `@osantana`_ and `@LucianU`_ for their innovation, as well as `@hackebrot`_ for fixing the Windows problems (#132, #184, #425). * Added `specify output directory`, thanks to `@tony`_ and `@hackebrot`_ (#531, #452). * Abort template rendering if the project output directory already exists, thanks to `@lgp171188`_ (#470, #471). * Add a flag to overwrite existing output directory, thanks to `@lgp171188`_ for the implementation (#495) and `@schacki`_, `@ionelmc`_, `@pydanny`_ and `@hackebrot`_ for submitting issues and code reviews (#475, #493). * Remove test command in favor of tox, thanks to `@hackebrot`_ (#480). * Allow cookiecutter invocation, even without installing it, via ``python -m cookiecutter.cli``, thanks to `@vincentbernat`_ and `@hackebrot`_ (#449, #487). * Improve the type detection handler for online and offline repositories, thanks to `@charlax`_ (#490). * Add replay feature, thanks to `@hackebrot`_ (#501). * Be more precise when raising an error for an invalid user config file, thanks to `@vaab`_ and `@hackebrot`_ (#378, #528). * Added official Python 3.5 support, thanks to `@pydanny`_ and `@hackebrot`_ (#522). * Added support for *choice* variables and switch to click style prompts, thanks to `@hackebrot`_ (#441, #455). Other Changes: * Updated click requirement to < 6.0, thanks to `@pydanny`_ (#473). * Added landscape.io flair, thanks to `@michaeljoseph`_ (#439). * Descriptions of PEP8 specifications and milestone management, thanks to `@michaeljoseph`_ (#440). * Added alternate installation options in the documentation, thanks to `@pydanny`_ (#117, #315). * The test of the `which()` function now tests against the `date` command, thanks to `@vincentbernat`_ (#446) * Ensure file handles in setup.py are closed using with statement, thanks to `@svisser`_ (#280). * Removed deprecated and fully extraneous `compat.is_exe()` function, thanks to `@hackebrot`_ (#485). * Disabled sudo in .travis, thanks to `@hackebrot`_ (#482). * Switched to shields.io for problematic badges, thanks to `@pydanny`_ (#491). * Added whichcraft and removed ``compat.which()``, thanks to `@pydanny`_ (#511). * Changed to export tox environment variables to codecov, thanks to `@maiksensi`_. (#508). * Moved to using click version command, thanks to `@hackebrot`_ (#489). * Don't use unicode_literals to please click, thanks to `@vincentbernat`_ (#503). * Remove warning for Python 2.6 from __init__.py, thanks to `@hackebrot`_. * Removed `compat.py` module, thanks to `@hackebrot`_. * Added `future` to requirements, thanks to `@hackebrot`_. * Fixed problem where expanduser does not resolve "~" correctly on windows 10 using tox, thanks to `@maiksensi`_. (#527) * Added more cookiecutter templates to the mix: * `cookiecutter-beamer`_ by `@luismartingil`_ (#307) * `cookiecutter-pytest-plugin`_ by `@pytest-dev`_ and `@hackebrot`_ (#481) * `cookiecutter-csharp-objc-binding`_ by `@SandyChapman`_ (#460) * `cookiecutter-flask-foundation`_ by `@JackStouffer`_ (#457) * `cookiecutter-tryton`_ by `@fulfilio`_ (#465) * `cookiecutter-tapioca`_ by `@vintasoftware`_ (#496) * `cookiecutter-sublime-text-3-plugin`_ by `@kkujawinski`_ (#500) * `cookiecutter-muffin`_ by `@drgarcia1986`_ (#494) * `cookiecutter-django-rest`_ by `@agconti`_ (#520) * `cookiecutter-es6-boilerplate`_ by `@agconti`_ (#521) * `cookiecutter-tampermonkey`_ by `@christabor`_ (#516) * `cookiecutter-wagtail`_ by `@torchbox`_ (#533) .. _`@maiksensi`: https://github.com/maiksensi .. _`copy without render`: http://cookiecutter.readthedocs.org/en/latest/advanced_usage.html#copy-without-render .. _`@osantana`: https://github.com/osantana .. _`@LucianU`: https://github.com/LucianU .. _`@svisser`: https://github.com/svisser .. _`@lgp171188`: https://github.com/lgp171188 .. _`@SandyChapman`: https://github.com/SandyChapman .. _`@JackStouffer`: https://github.com/JackStouffer .. _`@fulfilio`: https://github.com/fulfilio .. _`@vintasoftware`: https://github.com/vintasoftware .. _`@kkujawinski`: https://github.com/kkujawinski .. _`@charlax`: https://github.com/charlax .. _`@drgarcia1986`: https://github.com/drgarcia1986 .. _`@agconti`: https://github.com/agconti .. _`@vaab`: https://github.com/vaab .. _`@christabor`: https://github.com/christabor .. _`@torchbox`: https://github.com/torchbox .. _`@tony`: https://github.com/tony .. _`cookiecutter-beamer`: https://github.com/luismartingil/cookiecutter-beamer .. _`@luismartingil`: https://github.com/luismartingil .. _`cookiecutter-pytest-plugin`: https://github.com/pytest-dev/cookiecutter-pytest-plugin .. _`@pytest-dev`: https://github.com/pytest-dev .. _`cookiecutter-csharp-objc-binding`: https://github.com/SandyChapman/cookiecutter-csharp-objc-binding .. _`cookiecutter-flask-foundation`: https://github.com/JackStouffer/cookiecutter-Flask-Foundation .. _`cookiecutter-tryton`: https://github.com/fulfilio/cookiecutter-tryton .. _`cookiecutter-tapioca`: https://github.com/vintasoftware/cookiecutter-tapioca .. _`cookiecutter-sublime-text-3-plugin`: https://github.com/kkujawinski/cookiecutter-sublime-text-3-plugin .. _`cookiecutter-muffin`: https://github.com/drgarcia1986/cookiecutter-muffin .. _`cookiecutter-django-rest`: https://github.com/agconti/cookiecutter-django-rest .. _`cookiecutter-es6-boilerplate`: https://github.com/agconti/cookiecutter-es6-boilerplate .. _`cookiecutter-tampermonkey`: https://github.com/christabor/cookiecutter-tampermonkey .. _`cookiecutter-wagtail`: https://github.com/torchbox/cookiecutter-wagtail 1.0.0 (2015-03-13) Chocolate Chip ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The goals of this release was to formally remove support for Python 2.6 and continue the move to using py.test. Features: * Convert the unittest suite to py.test for the sake of comprehensibility, thanks to `@hackebrot`_ (#322, #332, #334, #336, #337, #338, #340, #341, #343, #345, #347, #351, #412, #413, #414). * Generate pytest coverage, thanks to `@michaeljoseph`_ (#326). * Documenting of Pull Request merging and HISTORY.rst maintenance, thanks to `@michaeljoseph`_ (#330). * Large expansions to the tutorials thanks to `@hackebrot`_ (#384) * Switch to using Click for command-line options, thanks to `@michaeljoseph`_ (#391, #393). * Added support for working with private repos, thanks to `@marctc`_ (#265). * Wheel configuration thanks to `@michaeljoseph`_ (#118). Other Changes: * Formally removed support for 2.6, thanks to `@pydanny`_ (#201). * Moved to codecov for continuous integration test coverage and badges, thanks to `@michaeljoseph`_ (#71, #369). * Made JSON parsing errors easier to debug, thanks to `@rsyring`_ and `@mark0978`_ (#355, #358, #388). * Updated to Jinja 2.7 or higher in order to control trailing new lines in templates, thanks to `@sfermigier`_ (#356). * Tweaked flake8 to ignore e731, thanks to `@michaeljoseph`_ (#390). * Fixed failing Windows tests and corrected AppVeyor badge link thanks to `@msabramo`_ (#403). * Added more Cookiecutters to the list: * `cookiecutter-scala-spark`_ by `@jpzk`_ * `cookiecutter-atari2600`_ by `@joeyjoejoejr`_ * `cookiecutter-bottle`_ by `@avelino`_ * `cookiecutter-latex-article`_ by `@Kreger51`_ * `cookiecutter-django-rest-framework`_ by `@jpadilla`_ * `cookiedozer`_ by `@hackebrot`_ .. _`@msabramo`: https://github.com/msabramo .. _`@marctc`: https://github.com/marctc .. _`cookiedozer`: https://github.com/hackebrot/cookiedozer .. _`@jpadilla`: https://github.com/jpadilla .. _`cookiecutter-django-rest-framework`: https://github.com/jpadilla/cookiecutter-django-rest-framework .. _`cookiecutter-latex-article`: https://github.com/Kreger51/cookiecutter-latex-article .. _`@Kreger51`: https://github.com/Kreger51 .. _`@rsyring`: https://github.com/rsyring .. _`@mark0978`: https://github.com/mark0978 .. _`cookiecutter-bottle`: https://github.com/avelino/cookiecutter-bottle .. _`@avelino`: https://github.com/avelino .. _`@joeyjoejoejr`: https://github.com/joeyjoejoejr .. _`cookiecutter-atari2600`: https://github.com/joeyjoejoejr/cookiecutter-atari2600 .. _`@sfermigier`: https://github.com/sfermigier .. _`cookiecutter-scala-spark`: https://github.com/jpzk/cookiecutter-scala-spark .. _`@jpzk`: https://github.com/jpzk 0.9.0 (2015-01-13) ~~~~~~~~~~~~~~~~~~~ The goals of this release were to add the ability to Jinja2ify the `cookiecutter.json` default values, and formally launch support for Python 3.4. Features: * Python 3.4 is now a first class citizen, thanks to everyone. * `cookiecutter.json` values are now rendered Jinja2 templates, thanks to @bollwyvl (#291). * Move to `py.test`, thanks to `@pfmoore`_ (#319) and `@ramiroluz`_ (#310). * Add `PendingDeprecation` warning for users of Python 2.6, as support for it is gone in Python 2.7, thanks to `@michaeljoseph`_ (#201). Bug Fixes: * Corrected typo in `Makefile`, thanks to `@inglesp`_ (#297). * Raise an exception when users don't have `git` or `hg` installed, thanks to `@pydanny`_ (#303). Other changes: * Creation of `gitter`_ account for logged chat, thanks to `@michaeljoseph`_. * Added ReadTheDocs badge, thanks to `@michaeljoseph`_. * Added AppVeyor badge, thanks to `@pydanny`_ * Documentation and PyPI trove classifier updates, thanks to `@thedrow`_ (#323 and #324) .. _`gitter`: https://gitter.im/audreyr/cookiecutter .. _`@inglesp`: https://github.com/inglesp .. _`@ramiroluz`: https://github.com/ramiroluz .. _`@thedrow`: https://github.com/thedrow .. _`@hackebrot`: https://github.com/hackebrot 0.8.0 (2014-10-30) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The goal of this release was to allow for injection of extra context via the Cookiecutter API, and to fix minor bugs. Features: * `cookiecutter()` now takes an optional `extra_context` parameter, thanks to `@michaeljoseph`_, `@fcurella`_, `@aventurella`_, `@emonty`_, `@schacki`_, `@ryanolson`_, `@pfmoore`_, `@pydanny`_, `@audreyr`_ (#260). * Context is now injected into hooks, thanks to `@michaeljoseph`_ and `@dinopetrone`_. * Moved all Python 2/3 compatability code into `cookiecutter.compat`, making the eventual move to `six` easier, thanks to `@michaeljoseph`_ (#60, #102). * Added `cookiecutterrc` defined aliases for cookiecutters, thanks to `@pfmoore`_ (#246) * Added `flake8` to tox to check for pep8 violations, thanks to `@natim`_. Bug Fixes: * Newlines at the end of files are no longer stripped, thanks to `@treyhunner`_ (#183). * Cloning prompt suppressed by respecting the `no_input` flag, thanks to `@trustrachel`_ (#285) * With Python 3, input is no longer converted to bytes, thanks to `@uranusjr`_ (#98). Other Changes: * Added more Cookiecutters to the list: * `Python-iOS-template`_ by `@freakboy3742`_ * `Python-Android-template`_ by `@freakboy3742`_ * `cookiecutter-djangocms-plugin`_ by `@mishbahr`_ * `cookiecutter-pyvanguard`_ by `@robinandeer`_ .. _`Python-iOS-template`: https://github.com/pybee/Python-iOS-template .. _`Python-Android-template`: https://github.com/pybee/Python-Android-template .. _`cookiecutter-djangocms-plugin`: https://github.com/mishbahr/cookiecutter-djangocms-plugin .. _`cookiecutter-pyvanguard`: https://github.com/robinandeer/cookiecutter-pyvanguard .. _`@trustrachel`: https://github.com/trustrachel .. _`@robinandeer`: https://github.com/robinandeer .. _`@mishbahr`: https://github.com/mishbahr .. _`@freakboy3742`: https://github.com/freakboy3742 .. _`@treyhunner`: https://github.com/treyhunner .. _`@pfmoore`: https://github.com/pfmoore .. _`@fcurella`: https://github.com/fcurella .. _`@aventurella`: https://github.com/aventurella .. _`@emonty`: https://github.com/emonty .. _`@schacki`: https://github.com/schacki .. _`@ryanolson`: https://github.com/ryanolson .. _`@Natim`: https://github.com/Natim .. _`@dinopetrone`: https://github.com/dinopetrone 0.7.2 (2014-08-05) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The goal of this release was to fix cross-platform compatibility, primarily Windows bugs that had crept in during the addition of new features. As of this release, Windows is a first-class citizen again, now complete with continuous integration. Bug Fixes: * Fixed the contributing file so it displays nicely in Github, thanks to `@pydanny`_. * Updates 2.6 requirements to include simplejson, thanks to `@saxix`_. * Avoid unwanted extra spaces in string literal, thanks to `@merwok`_. * Fix `@unittest.skipIf` error on Python 2.6. * Let sphinx parse `:param:` properly by inserting newlines #213, thanks to `@mineo`_. * Fixed Windows test prompt failure by replacing stdin per `@cjrh`_ in #195. * Made rmtree remove readonly files, thanks to `@pfmoore`_. * Now using tox to run tests on Appveyor, thanks to `@pfmoore`_ (#241). * Fixed tests that assumed the system encoding was utf-8, thanks to `@pfmoore`_ (#242, #244). * Added a tox ini file that uses py.test, thanks to `@pfmoore`_ (#245). .. _`@merwok`: https://github.com/merwok .. _`@mineo`: https://github.com/mineo .. _`@cjrh`: https://github.com/cjrh Other Changes: * `@audreyr`_ formally accepted position as **BDFL of cookiecutter**. * Elevated `@pydanny`_, `@michaeljoseph`_, and `@pfmoore`_ to core committer status. * Added Core Committer guide, by `@audreyr`_. * Generated apidocs from `make docs`, by `@audreyr`_. * Added `contributing` command to the `make docs` function, by `@pydanny`_. * Refactored contributing documentation, included adding core committer instructions, by `@pydanny`_ and `@audreyr`_. * Do not convert input prompt to bytes, thanks to `@uranusjr`_ (#192). * Added troubleshooting info about Python 3.3 tests and tox. * Added documentation about command line arguments, thanks to `@saxix`_. * Style cleanups. * Added environment variable to disable network tests for environments without networking, thanks to `@vincentbernat`_. * Added Appveyor support to aid Windows integrations, thanks to `@pydanny`_ (#215). * CONTRIBUTING.rst is now generated via `make contributing`, thanks to `@pydanny`_ (#220). * Removed unnecessary endoing argument to `json.load`, thanks to `@pfmoore`_ (#234). * Now generating shell hooks dynamically for Unix/Windows portability, thanks to `@pfmoore`_ (#236). * Removed non-portable assumptions about directory structure, thanks to `@pfmoore`_ (#238). * Added a note on portability to the hooks documentation, thanks to `@pfmoore`_ (#239). * Replaced `unicode_open` with direct use of `io.open`, thanks to `@pfmoore`_ (#229). * Added more Cookiecutters to the list: * `cookiecutter-kivy`_ by `@hackebrot`_ * BoilerplatePP_ by `@Paspartout`_ * `cookiecutter-pypackage-minimal`_ by `@borntyping`_ * `cookiecutter-ansible-role`_ by `@iknite`_ * `cookiecutter-pylibrary`_ by `@ionelmc`_ * `cookiecutter-pylibrary-minimal`_ by `@ionelmc`_ .. _`cookiecutter-kivy`: https://github.com/hackebrot/cookiecutter-kivy .. _`cookiecutter-ansible-role`: https://github.com/iknite/cookiecutter-ansible-role .. _BoilerplatePP: https://github.com/Paspartout/BoilerplatePP .. _`cookiecutter-pypackage-minimal`: https://github.com/borntyping/cookiecutter-pypackage-minimal .. _`cookiecutter-pylibrary`: https://github.com/ionelmc/cookiecutter-pylibrary .. _`cookiecutter-pylibrary-minimal`: https://github.com/ionelmc/cookiecutter-pylibrary-minimal .. _`@Paspartout`: https://github.com/Paspartout .. _`@audreyr`: https://github.com/audreyr .. _`@borntyping`: https://github.com/borntyping .. _`@hackebrot`: https://github.com/hackebrot .. _`@iknite`: https://github.com/iknite .. _`@ionelmc`: https://github.com/ionelmc .. _`@michaeljoseph`: https://github.com/michaeljoseph .. _`@pfmoore`: https://github.com/pfmoore .. _`@pydanny`: https://github.com/pydanny .. _`@saxix`: https://github.com/saxix .. _`@uranusjr`: https://github.com/uranusjr 0.7.1 (2014-04-26) ~~~~~~~~~~~~~~~~~~~~~~~~~~ Bug fixes: * Use the current Python interpreter to run Python hooks, thanks to `@coderanger`_. * Include tests and documentation in source distribution, thanks to `@vincentbernat`_. * Fix various warnings and missing things in the docs (#129, #130), thanks to `@nedbat`_. * Add command line option to get version (#89), thanks to `@davedash`_ and `@cyberj`_. Other changes: * Add more Cookiecutters to the list: * `cookiecutter-avr`_ by `@solarnz`_ * `cookiecutter-tumblr-theme`_ by `@relekang`_ * `cookiecutter-django-paas`_ by `@pbacterio`_ .. _`@coderanger`: https://github.com/coderanger .. _`@vincentbernat`: https://github.com/vincentbernat .. _`@nedbat`: https://github.com/nedbat .. _`@davedash`: https://github.com/davedash .. _`@cyberj`: https://github.com/cyberj .. _`cookiecutter-avr`: https://github.com/solarnz/cookiecutter-avr .. _`@solarnz`: https://github.com/solarnz .. _`cookiecutter-tumblr-theme`: https://github.com/relekang/cookiecutter-tumblr-theme .. _`@relekang`: https://github.com/relekang .. _`cookiecutter-django-paas`: https://github.com/pbacterio/cookiecutter-django-paas .. _`@pbacterio`: https://github.com/pbacterio 0.7.0 (2013-11-09) ~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a release with significant improvements and changes. Please read through this list before you upgrade. New features: * Support for --checkout argument, thanks to `@foobacca`_. * Support for pre-generate and post-generate hooks, thanks to `@raphigaziano`_. Hooks are Python or shell scripts that run before and/or after your project is generated. * Support for absolute paths to cookiecutters, thanks to `@krallin`_. * Support for Mercurial version control system, thanks to `@pokoli`_. * When a cookiecutter contains invalid Jinja2 syntax, you get a better message that shows the location of the TemplateSyntaxError. Thanks to `@benjixx`_. * Can now prompt the user to enter values during generation from a local cookiecutter, thanks to `@ThomasChiroux`_. This is now always the default behavior. Prompts can also be supressed with `--no-input`. * Your cloned cookiecutters are stored by default in your `~/.cookiecutters/` directory (or Windows equivalent). The location is configurable. (This is a major change from the pre-0.7.0 behavior, where cloned cookiecutters were deleted at the end of project generation.) Thanks `@raphigaziano`_. * User config in a `~/.cookiecutterrc` file, thanks to `@raphigaziano`_. Configurable settings are `cookiecutters_dir` and `default_context`. * File permissions are now preserved during project generation, thanks to `@benjixx`_. Bug fixes: * Unicode issues with prompts and answers are fixed, thanks to `@s-m-i-t-a`_. * The test suite now runs on Windows, which was a major effort. Thanks to `@pydanny`_, who collaborated on this with me. Other changes: * Quite a bit of refactoring and API changes. * Lots of documentation improvements. Thanks `@sloria`_, `@alex`_, `@pydanny`_, `@freakboy3742`_, `@es128`_, `@rolo`_. * Better naming and organization of test suite. * A `CookiecutterCleanSystemTestCase` to use for unit tests affected by the user's config and cookiecutters directory. * Improvements to the project's Makefile. * Improvements to tests. Thanks `@gperetin`_, `@s-m-i-t-a`_. * Removal of `subprocess32` dependency. Now using non-context manager version of `subprocess.Popen` for Python 2 compatibility. * Removal of cookiecutter's `cleanup` module. * A bit of `setup.py` cleanup, thanks to `@oubiga`_. * Now depends on binaryornot 0.2.0. .. _`@foobacca`: https://github.com/foobacca/ .. _`@raphigaziano`: https://github.com/raphigaziano/ .. _`@gperetin`: https://github.com/gperetin/ .. _`@krallin`: https://github.com/krallin/ .. _`@pokoli`: https://github.com/pokoli/ .. _`@benjixx`: https://github.com/benjixx/ .. _`@ThomasChiroux`: https://github.com/ThomasChiroux/ .. _`@s-m-i-t-a`: https://github.com/s-m-i-t-a/ .. _`@sloria`: https://github.com/sloria/ .. _`@alex`: https://github.com/alex/ .. _`@es128`: https://github.com/es128/ .. _`@rolo`: https://github.com/rolo/ .. _`@oubiga`: https://github.com/oubiga/ 0.6.4 (2013-08-21) ~~~~~~~~~~~~~~~~~~ * Windows support officially added. * Fix TemplateNotFound Exception on Windows (#37). 0.6.3 (2013-08-20) ~~~~~~~~~~~~~~~~~~ * Fix copying of binary files in nested paths (#41), thanks to `@sloria`_. .. _`@sloria`: https://github.com/sloria/ 0.6.2 (2013-08-19) ~~~~~~~~~~~~~~~~~~ * Depend on Jinja2>=2.4 instead of Jinja2==2.7. * Fix errors on attempt to render binary files. Copy them over from the project template without rendering. * Fix Python 2.6/2.7 `UnicodeDecodeError` when values containing Unicode chars are in `cookiecutter.json`. * Set encoding in Python 3 `unicode_open()` to always be utf-8. 0.6.1 (2013-08-12) ~~~~~~~~~~~~~~~~~~ * Improved project template finding. Now looks for the occurrence of `{{`, `cookiecutter`, and `}}` in a directory name. * Fix help message for input_dir arg at command prompt. * Minor edge cases found and corrected, as a result of improved test coverage. 0.6.0 (2013-08-08) ~~~~~~~~~~~~~~~~~~ * Config is now in a single `cookiecutter.json` instead of in `json/`. * When you create a project from a git repo template, Cookiecutter prompts you to enter custom values for the fields defined in `cookiecutter.json`. 0.5 (2013-07-28) ~~~~~~~~~~~~~~~~~~ * Friendlier, more simplified command line usage:: # Create project from the cookiecutter-pypackage/ template $ cookiecutter cookiecutter-pypackage/ # Create project from the cookiecutter-pypackage.git repo template $ cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git * Can now use Cookiecutter from Python as a package:: from cookiecutter.main import cookiecutter # Create project from the cookiecutter-pypackage/ template cookiecutter('cookiecutter-pypackage/') # Create project from the cookiecutter-pypackage.git repo template cookiecutter('https://github.com/audreyr/cookiecutter-pypackage.git') * Internal refactor to remove any code that changes the working directory. 0.4 (2013-07-22) ~~~~~~~~~~~~~~~~~~ * Only takes in one argument now: the input directory. The output directory is generated by rendering the name of the input directory. * Output directory cannot be the same as input directory. 0.3 (2013-07-17) ~~~~~~~~~~~~~~~~~~ * Takes in command line args for the input and output directories. 0.2.1 (2013-07-17) ~~~~~~~~~~~~~~~~~~ * Minor cleanup. 0.2 (2013-07-17) ~~~~~~~~~~~~~~~~~~ Bumped to "Development Status :: 3 - Alpha". * Works with any type of text file. * Directory names and filenames can be templated. 0.1.0 (2013-07-11) ~~~~~~~~~~~~~~~~~~ * First release on PyPI. Roadmap ------- https://github.com/audreyr/cookiecutter/milestones?direction=desc&sort=due_date&state=open cookiecutter-1.3.0/LICENSE000066400000000000000000000026751262047200400152270ustar00rootroot00000000000000Copyright (c) 2013-2015, Audrey Roy All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * Neither the name of border nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. cookiecutter-1.3.0/MANIFEST.in000066400000000000000000000003641262047200400157510ustar00rootroot00000000000000include AUTHORS.rst include CONTRIBUTING.rst include HISTORY.rst include LICENSE include README.rst recursive-include tests * recursive-exclude * __pycache__ recursive-exclude * *.py[co] recursive-include docs *.rst conf.py Makefile make.bat cookiecutter-1.3.0/Makefile000066400000000000000000000044051262047200400156530ustar00rootroot00000000000000.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" @echo "clean-pyc - remove Python file artifacts" @echo "lint - check style with flake8" @echo "test - run tests quickly with the default Python" @echo "test-all - run tests on every Python version with tox" @echo "coverage - check code coverage quickly with the default Python" @echo "docs - generate Sphinx HTML documentation, including API docs" @echo "release - package and upload a release" @echo "sdist - package" clean: clean-build clean-pyc clean-build: rm -fr build/ rm -fr dist/ rm -fr *.egg-info clean-pyc: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + find . -name '*~' -exec rm -f {} + lint: flake8 cookiecutter tests test: tox -e py test-all: tox coverage: tox -e cov-report $(BROWSER) htmlcov/index.html docs: rm -f docs/cookiecutter.rst sphinx-apidoc -o docs/ cookiecutter rm -f docs/modules.rst $(MAKE) -C docs clean $(MAKE) -C docs html make contributing $(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 sdist: clean python setup.py sdist ls -l dist wheel: clean python setup.py bdist_wheel ls -l dist contributing: rm CONTRIBUTING.rst touch CONTRIBUTING.rst cat docs/contributing.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/types_of_contributions.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_setup.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_guidelines.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_testing.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/core_committer_guide.rst >> CONTRIBUTING.rst echo "\r\rAutogenerated from the docs via \`make contributing\`" >> CONTRIBUTING.rst echo "WARNING: Don't forget to replace any :ref: statements with literal names" cookiecutter-1.3.0/README.rst000066400000000000000000000522271262047200400157070ustar00rootroot00000000000000============= Cookiecutter ============= .. image:: https://img.shields.io/pypi/v/cookiecutter.svg :target: https://pypi.python.org/pypi/cookiecutter .. image:: https://travis-ci.org/audreyr/cookiecutter.png?branch=master :target: https://travis-ci.org/audreyr/cookiecutter .. image:: https://ci.appveyor.com/api/projects/status/github/audreyr/cookiecutter?branch=master :target: https://ci.appveyor.com/project/audreyr/cookiecutter/branch/master .. image:: https://img.shields.io/pypi/dm/cookiecutter.svg :target: https://pypi.python.org/pypi/cookiecutter .. image:: https://codecov.io/github/audreyr/cookiecutter/coverage.svg?branch=master :target: https://codecov.io/github/audreyr/cookiecutter?branch=master .. image:: https://badges.gitter.im/Join Chat.svg :target: https://gitter.im/audreyr/cookiecutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge .. image:: https://readthedocs.org/projects/cookiecutter/badge/?version=latest :target: https://readthedocs.org/projects/cookiecutter/?badge=latest :alt: Documentation Status .. image:: https://landscape.io/github/audreyr/cookiecutter/master/landscape.svg?style=flat :target: https://landscape.io/github/audreyr/cookiecutter/master :alt: Code Health A command-line utility that creates projects from **cookiecutters** (project templates), e.g. creating a Python package project from a Python package project template. * Documentation: http://cookiecutter.rtfd.org * GitHub: https://github.com/audreyr/cookiecutter * Free software: BSD license * PyPI: https://pypi.python.org/pypi/cookiecutter .. image:: https://raw.github.com/audreyr/cookiecutter/aa309b73bdc974788ba265d843a65bb94c2e608e/cookiecutter_medium.png Features -------- Did someone say features? * Cross-platform: Windows, Mac, and Linux are officially supported. * Works with Python 2.7, 3.3, 3.4, 3.5, and PyPy. *(But you don't have to know/write Python code to use Cookiecutter.)* * Project templates can be in any programming language or markup format: Python, JavaScript, Ruby, CoffeeScript, RST, Markdown, CSS, HTML, you name it. You can use multiple languages in the same project template. * Simple command line usage: .. code-block:: bash # Create project from the cookiecutter-pypackage.git repo template # You'll be prompted to enter values. # Then it'll create your Python package in the current working directory, # based on those values. $ cookiecutter https://github.com/audreyr/cookiecutter-pypackage # For the sake of brevity, repos on GitHub can just use the 'gh' prefix $ cookiecutter gh:audreyr/cookiecutter-pypackage * Can also use it at the command line with a local template: .. code-block:: bash # Create project in the current working directory, from the local # cookiecutter-pypackage/ template $ cookiecutter cookiecutter-pypackage/ * Or use it from Python: .. code-block:: python from cookiecutter.main import cookiecutter # Create project from the cookiecutter-pypackage/ template cookiecutter('cookiecutter-pypackage/') # Create project from the cookiecutter-pypackage.git repo template cookiecutter('https://github.com/audreyr/cookiecutter-pypackage.git') * Directory names and filenames can be templated. For example:: {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}.py * Supports unlimited levels of directory nesting. * 100% of templating is done with Jinja2. This includes file and directory names. * Simply define your template variables in a `cookiecutter.json` file. For example: .. code-block:: json { "full_name": "Audrey Roy", "email": "audreyr@gmail.com", "project_name": "Complexity", "repo_name": "complexity", "project_short_description": "Refreshingly simple static site generator.", "release_date": "2013-07-10", "year": "2013", "version": "0.1.1" } * Unless you suppress it with `--no-input`, you are prompted for input: - Prompts are the keys in `cookiecutter.json`. - Default responses are the values in `cookiecutter.json`. - Prompts are shown in order. * Cross-platform support for `~/.cookiecutterrc` files: .. code-block:: yaml default_context: full_name: "Audrey Roy" email: "audreyr@gmail.com" github_username: "audreyr" cookiecutters_dir: "~/.cookiecutters/" * Cookiecutters (cloned Cookiecutter project templates) are put into `~/.cookiecutters/` by default, or cookiecutters_dir if specified. * You can use local cookiecutters, or remote cookiecutters directly from Git repos or from Mercurial repos on Bitbucket. * Default context: specify key/value pairs that you want used as defaults whenever you generate a project * Direct access to the Cookiecutter API allows for injection of extra context. * Pre- and post-generate hooks: Python or shell scripts to run before or after generating a project. * Paths to local projects can be specified as absolute or relative. * Projects are always generated to your current directory. Available Cookiecutters ----------------------- Here is a list of **cookiecutters** (aka Cookiecutter project templates) for you to use or fork. Make your own, then submit a pull request adding yours to this list! Python ~~~~~~ * `cookiecutter-pypackage`_: `@audreyr`_'s ultimate Python package project template. * `cookiecutter-flask`_ : A Flask template with Bootstrap 3, starter templates, and working user registration. * `cookiecutter-flask-foundation`_ : Flask Template with caching, forms, sqlalchemy and unit-testing. * `cookiecutter-bottle`_ : A cookiecutter template for creating reusable Bottle projects quickly. * `cookiecutter-openstack`_: A template for an OpenStack project. * `cookiecutter-docopt`_: A template for a Python command-line script that uses `docopt`_ for arguments parsing. * `cookiecutter-quokka-module`_: A template to create a blueprint module for Quokka Flask CMS. * `cookiecutter-kivy`_: A template for NUI applications built upon the kivy python-framework. * `cookiedozer`_: A template for Python Kivy apps ready to be deployed to android devices with Buildozer. * `cookiecutter-pypackage-minimal`_: A mimimal Python package template. * `cookiecutter-ansible-role`_: A template to create ansible roles. Forget about file creation and focus on actions. * `cookiecutter-pylibrary`_: An intricate template designed to quickly get started with good testing and packaging (working configuration for Tox, Pytest, Travis-CI, Coveralls, AppVeyor, Sphinx docs, isort, bumpversion, packaging checks etc). * `cookiecutter-pyvanguard`_: A template for cutting edge Python development. `Invoke`_, pytest, bumpversion, and Python 2/3 compatability. * `Python-iOS-template`_: A template to create a Python project that will run on iOS devices. * `Python-Android-template`_: A template to create a Python project that will run on Android devices. * `cookiecutter-tryton`_: A template for creating tryton modules. * `cookiecutter-pytest-plugin`_: Minimal Cookiecutter template for authoring `pytest`_ plugins that help you to write better programs. * `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 ^^^^^^^^^^^^^ * `cookiecutter-django`_: A bleeding edge Django project template with Bootstrap 4, customizable users app, starter templates, working user registration, celery setup, and much more. * `cookiecutter-django-rest`_: For creating REST apis for mobile and web applications. * `cookiecutter-simple-django`_: A cookiecutter template for creating reusable Django projects quickly. * `cookiecutter-djangopackage`_: A template designed to create reusable third-party PyPI friendly Django apps. Documentation is written in tutorial format. * `cookiecutter-django-cms`_: A template for Django CMS with simple Bootstrap 3 template. It has a quick start and deploy documentation. * `cookiecutter-djangocms-plugin`_: A template to get started with custom plugins for django-cms * `cookiecutter-django-crud`_: A template to create a Django app with boilerplate CRUD around a model including a factory and tests. * `cookiecutter-django-lborgav`_: Another cookiecutter template for Django project with Booststrap 3 and FontAwesome 4 * `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 ~~ * `bootstrap.c`_: A template for simple projects written in C with autotools. * `cookiecutter-avr`_: A template for avr development. C++ ~~~ * `BoilerplatePP`_: A simple cmake template with unit testing for projects written in C++. C# ~~ * `cookiecutter-csharp-objc-binding`_: A template for generating a C# binding project for binding an Objective-C static library. .. _`cookiecutter-csharp-objc-binding`: https://github.com/SandyChapman/cookiecutter-csharp-objc-binding Common Lisp ~~~~~~~~~~~ * `cookiecutter-cl-project`_: A template for Common Lisp project with bootstrap script and Slime integration. JS ~~ * `cookiecutter-es6-boilerplate`_: A cookiecutter for front end projects in ES6. * `cookiecutter-jquery`_: A jQuery plugin project template based on jQuery Boilerplate. * `cookiecutter-jswidget`_: A project template for creating a generic front-end, non-jQuery JS widget packaged for multiple JS packaging systems. * `cookiecutter-component`_: A template for a Component JS package. * `cookiecutter-tampermonkey`_: A template for a TamperMonkey browser script. LaTeX/XeTeX ~~~~~~~~~~~ * `pandoc-talk`_: A cookiecutter template for giving talks with pandoc and XeTeX. * `cookiecutter-latex-article`_: A LaTeX template geared towards academic use. * `cookiecutter-beamer`_: A template for a LaTeX Beamer presentation. Berkshelf-Vagrant ~~~~~~~~~~~~~~~~~ * `slim-berkshelf-vagrant`_: A simple cookiecutter template with sane cookbook defaults for common vagrant/berkshelf cookbooks. HTML ~~~~ * `cookiecutter-complexity`_: A cookiecutter for a Complexity static site with Bootstrap 3. * `cookiecutter-tumblr-theme`_: A cookiecutter for a Tumblr theme project with GruntJS as concatination tool. .. _`cookiecutter-django-rest`: https://github.com/agconti/cookiecutter-django-rest .. _`cookiecutter-es6-boilerplate`: https://github.com/agconti/cookiecutter-es6-boilerplate .. _`cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage .. _`cookiecutter-jquery`: https://github.com/audreyr/cookiecutter-jquery .. _`cookiecutter-flask`: https://github.com/sloria/cookiecutter-flask .. _`cookiecutter-flask-foundation`: https://github.com/JackStouffer/cookiecutter-Flask-Foundation .. _`cookiecutter-bottle`: https://github.com/avelino/cookiecutter-bottle .. _`cookiecutter-simple-django`: https://github.com/marcofucci/cookiecutter-simple-django .. _`cookiecutter-django`: https://github.com/pydanny/cookiecutter-django .. _`cookiecutter-djangopackage`: https://github.com/pydanny/cookiecutter-djangopackage .. _`cookiecutter-django-cms`: https://github.com/palazzem/cookiecutter-django-cms .. _`cookiecutter-djangocms-plugin`: https://github.com/mishbahr/cookiecutter-djangocms-plugin .. _`cookiecutter-django-crud`: https://github.com/wildfish/cookiecutter-django-crud .. _`cookiecutter-quokka-module`: https://github.com/pythonhub/cookiecutter-quokka-module .. _`cookiecutter-django-lborgav`: https://github.com/lborgav/cookiecutter-django .. _`cookiecutter-django-paas`: https://github.com/pbacterio/cookiecutter-django-paas .. _`cookiecutter-kivy`: https://github.com/hackebrot/cookiecutter-kivy .. _`cookiedozer`: https://github.com/hackebrot/cookiedozer .. _`cookiecutter-pypackage-minimal`: https://github.com/borntyping/cookiecutter-pypackage-minimal .. _`cookiecutter-ansible-role`: https://github.com/iknite/cookiecutter-ansible-role .. _`bootstrap.c`: https://github.com/vincentbernat/bootstrap.c .. _`BoilerplatePP`: https://github.com/Paspartout/BoilerplatePP .. _`cookiecutter-openstack`: https://github.com/openstack-dev/cookiecutter .. _`cookiecutter-component`: https://github.com/audreyr/cookiecutter-component .. _`cookiecutter-tampermonkey`: https://github.com/christabor/cookiecutter-tampermonkey .. _`cookiecutter-docopt`: https://github.com/sloria/cookiecutter-docopt .. _`docopt`: http://docopt.org/ .. _`cookiecutter-jswidget`: https://github.com/audreyr/cookiecutter-jswidget .. _`pandoc-talk`: https://github.com/larsyencken/pandoc-talk .. _`cookiecutter-latex-article`: https://github.com/Kreger51/cookiecutter-latex-article .. _`cookiecutter-complexity`: https://github.com/audreyr/cookiecutter-complexity .. _`cookiecutter-cl-project`: https://github.com/svetlyak40wt/cookiecutter-cl-project .. _`slim-berkshelf-vagrant`: https://github.com/mahmoudimus/cookiecutter-slim-berkshelf-vagrant .. _`cookiecutter-avr`: https://github.com/solarnz/cookiecutter-avr .. _`cookiecutter-tumblr-theme`: https://github.com/relekang/cookiecutter-tumblr-theme .. _`cookiecutter-pylibrary`: https://github.com/ionelmc/cookiecutter-pylibrary .. _`cookiecutter-pyvanguard`: https://github.com/robinandeer/cookiecutter-pyvanguard .. _`Python-iOS-template`: https://github.com/pybee/Python-iOS-template .. _`Python-Android-template`: https://github.com/pybee/Python-Android-template .. _`Invoke`: http://invoke.readthedocs.org/en/latest/ .. _`cookiecutter-django-rest-framework`: https://github.com/jpadilla/cookiecutter-django-rest-framework .. _`cookiecutter-tryton`: https://github.com/fulfilio/cookiecutter-tryton .. _`cookiecutter-beamer`: https://github.com/luismartingil/cookiecutter-beamer .. _`cookiecutter-pytest-plugin`: https://github.com/pytest-dev/cookiecutter-pytest-plugin .. _`pytest`: http://pytest.org/latest/ .. _`cookiecutter-tapioca`: https://github.com/vintasoftware/cookiecutter-tapioca .. _`tapioca-wrapper`: https://github.com/vintasoftware/tapioca-wrapper .. _`cookiecutter-sublime-text-3-plugin`: https://github.com/kkujawinski/cookiecutter-sublime-text-3-plugin .. _`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 ~~~~~ * `cookiecutter-scala-spark`_: A cookiecutter template for Apache Spark applications written in Scala. .. _`cookiecutter-scala-spark`: https://github.com/jpzk/cookiecutter-scala-spark 6502 Assembly ~~~~~~~~~~~~~ * `cookiecutter-atari2600`_: A cookiecutter template for Atari2600 projects. .. _`cookiecutter-atari2600`: https://github.com/joeyjoejoejr/cookiecutter-atari2600 Similar projects ---------------- * `Paste`_ has a create option that creates a skeleton project. * `Diecutter`_: an API service that will give you back a configuration file from a template and variables. * `Django`_'s `startproject` and `startapp` commands can take in a `--template` option. * `python-packager`_: Creates Python packages from its own template, with configurable options. * `Yeoman`_ has a Rails-inspired generator system that provides scaffolding for apps. * `Pyramid`_'s `pcreate` command for creating Pyramid projects from scaffold templates. * `mr.bob`_ is a filesystem template renderer, meant to deprecate tools such as paster and templer. * `grunt-init`_ used to be built into Grunt and is now a standalone scaffolding tool to automate project creation. * `scaffolt`_ consumes JSON generators with Handlebars support. * `init-skeleton`_ clones or copies a repository, executes npm install and bower install and removes the .git directory. * `Cog`_ python-based code generation toolkit developed by Ned Batchelder * `Skaffold`_ python and json config based django/MVC generator, with some add-ons and integrations. .. _`Paste`: http://pythonpaste.org/script/#paster-create .. _`Diecutter`: https://github.com/novagile/diecutter .. _`Django`: https://docs.djangoproject.com/en/1.5/ref/django-admin/#django-admin-startproject .. _`python-packager`: https://github.com/fcurella/python-packager .. _`Yeoman`: https://github.com/yeoman/generator .. _`Pyramid`: http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/scaffolding.html .. _`mr.bob`: https://github.com/iElectric/mr.bob .. _`grunt-init`: https://github.com/gruntjs/grunt-init .. _`scaffolt`: https://github.com/paulmillr/scaffolt .. _`init-skeleton`: https://github.com/paulmillr/init-skeleton .. _`Cog`: https://bitbucket.org/ned/cog .. _`Skaffold`: https://github.com/christabor/Skaffold Community --------- The core committer team is `@audreyr`_, `@pydanny`_, `@michaeljoseph`_, `@pfmoore`_, and `@hackebrot`_. We welcome you and invite you to participate. Stuck? Try one of the following: * See the `Troubleshooting`_ page. * Ask for help on `Stack Overflow`_. * You are strongly encouraged to `file an issue`_ about the problem, even if it's just "I can't get it to work on this cookiecutter" with a link to your cookiecutter. Don't worry about naming/pinpointing the issue properly. * Ask for help on `Gitter`_ if you must (but please try one of the other options first, so that others can benefit from the discussion) Development on Cookiecutter is community-driven: * Huge thanks to all the `contributors`_ who have pitched in to help make Cookiecutter an even better tool. * Everyone is invited to contribute. Read the `contributing instructions`_, then get started. Connect with other Cookiecutter contributors and users on `Gitter`_: * https://gitter.im/audreyr/cookiecutter (note: due to work and commitments, a core committer might not always be available) Encouragement is unbelievably motivating. If you want more work done on Cookiecutter, show support: * Thank a core committer for their efforts. * Star `Cookiecutter on GitHub`_. * Join the `Cookiecutter Gittip community`_. Got criticism or complaints? * `File an issue`_ so that Cookiecutter can be improved. Be friendly and constructive about what could be better. Make detailed suggestions. * **Keep us in the loop so that we can help.** For example, if you are discussing problems with Cookiecutter on a mailing list, `file an issue`_ where you link to the discussion thread and/or cc at least 1 core committer on the email. * Be encouraging. A comment like "This function ought to be rewritten like this" is much more likely to result in action than a comment like "Eww, look how bad this function is." Waiting for a response to an issue/question? * Be patient and persistent. All issues are on the core committer team's radar and will be considered thoughtfully, but we have a lot of issues to work through. If urgent, it's fine to ping a core committer in the issue with a reminder. * Ask others to comment, discuss, review, etc. * Search the Cookiecutter repo for issues related to yours. * 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 --------------- Everyone interacting in the Cookiecutter project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. .. _`PyPA Code of Conduct`: https://www.pypa.io/en/latest/code-of-conduct/ .. _`Cookiecutter on GitHub`: https://github.com/audreyr/cookiecutter .. _`Troubleshooting`: http://cookiecutter.readthedocs.org/en/latest/troubleshooting.html .. _`contributors`: https://github.com/audreyr/cookiecutter/blob/master/AUTHORS.rst .. _`contributing instructions`: https://github.com/audreyr/cookiecutter/blob/master/CONTRIBUTING.rst .. _`Stack Overflow`: http://stackoverflow.com/ .. _`File an issue`: https://github.com/audreyr/cookiecutter/issues?state=open .. _`Cookiecutter Gittip community`: https://www.gittip.com/for/cookiecutter/ .. _`@audreyr`: https://github.com/audreyr .. _`@pydanny`: https://github.com/pydanny .. _`@michaeljoseph`: https://github.com/michaeljoseph .. _`@pfmoore`: https://github.com/pfmoore .. _`@hackebrot`: https://github.com/hackebrot .. _`Gitter`: https://gitter.im/audreyr/cookiecutter cookiecutter-1.3.0/appveyor.yml000066400000000000000000000021241262047200400165770ustar00rootroot00000000000000# What Python version is installed where: # http://www.appveyor.com/docs/installed-software#python environment: matrix: - 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\'))\"" install: - "%PYTHON%/Scripts/easy_install -U pip" - "%PYTHON%/Scripts/pip install tox" - "%PYTHON%/Scripts/pip install wheel" build: false # Not a C# project, build stuff at the test step instead. test_script: - "%PYTHON%/Scripts/tox -e %TOX_ENV%" after_test: - "%PYTHON%/python setup.py bdist_wheel" - ps: "ls dist" artifacts: - path: dist\* #on_success: # - TODO: upload the content of dist/*.whl to a public wheelhouse cookiecutter-1.3.0/cookiecutter/000077500000000000000000000000001262047200400167105ustar00rootroot00000000000000cookiecutter-1.3.0/cookiecutter/__init__.py000077500000000000000000000002101262047200400210150ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter ------------ Main package for Cookiecutter. """ __version__ = '1.3.0' cookiecutter-1.3.0/cookiecutter/cli.py000077500000000000000000000061341262047200400200400ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.cli ----------------- Main `cookiecutter` CLI. """ import os import sys 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, FailedHookException ) logger = logging.getLogger(__name__) def version_msg(): python_version = sys.version[:3] location = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) message = u'Cookiecutter %(version)s from {} (Python {})' return message.format(location, python_version) @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( u'--no-input', is_flag=True, help=u'Do not prompt for parameters and only use cookiecutter.json ' u'file content', ) @click.option( u'-c', u'--checkout', help=u'branch, tag or commit to checkout after git clone', ) @click.option( '-v', '--verbose', is_flag=True, help='Print debug information', default=False ) @click.option( u'--replay', is_flag=True, help=u'Do not prompt for parameters and only use information entered ' u'previously', ) @click.option( u'-f', u'--overwrite-if-exists', is_flag=True, help=u'Overwrite the contents of the output directory if it already exists' ) @click.option( u'-o', u'--output-dir', default='.', type=click.Path(), help=u'Where to output the generated project dir into' ) @click.option( u'--config-file', type=click.Path(), default=USER_CONFIG_PATH, help=u'User configuration file' ) @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, config_file, default_config): """Create a project from a Cookiecutter project template (TEMPLATE).""" if verbose: logging.basicConfig( format=u'%(levelname)s %(filename)s: %(message)s', level=logging.DEBUG ) else: # Log info and above to console logging.basicConfig( format=u'%(levelname)s: %(message)s', level=logging.INFO ) 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, config_file=user_config ) except (OutputDirExistsException, InvalidModeException, FailedHookException) as e: click.echo(e) sys.exit(1) if __name__ == "__main__": main() cookiecutter-1.3.0/cookiecutter/config.py000077500000000000000000000047751262047200400205470ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.config ------------------- Global configuration handling """ from __future__ import unicode_literals import copy import logging import os import io try: import ruamel.yaml as yaml except ImportError: import yaml from .exceptions import ConfigDoesNotExistException 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/'), 'default_context': {} } def get_config(config_path): """ Retrieve the config from the specified path, returning it as a config dict. """ if not os.path.exists(config_path): raise ConfigDoesNotExistException logger.debug('config_path is {0}'.format(config_path)) with io.open(config_path, encoding='utf-8') as file_handle: try: yaml_dict = yaml.safe_load(file_handle) except yaml.scanner.ScannerError as e: raise InvalidConfiguration( '{0} is not a valid YAML file: line {1}: {2}'.format( config_path, e.problem_mark.line, e.problem)) config_dict = copy.copy(DEFAULT_CONFIG) config_dict.update(yaml_dict) return config_dict 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. """ # 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) cookiecutter-1.3.0/cookiecutter/exceptions.py000077500000000000000000000041341262047200400214500ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.exceptions ----------------------- All exceptions used in the Cookiecutter code base are defined here. """ class CookiecutterException(Exception): """ Base exception class. All Cookiecutter-specific exceptions should subclass this class. """ class NonTemplatedInputDirException(CookiecutterException): """ Raised when a project's input dir is not templated. The name of the input directory should always contain a string that is rendered to something else, so that input_dir != output_dir. """ class UnknownTemplateDirException(CookiecutterException): """ Raised when Cookiecutter cannot determine which directory is the project template, e.g. more than one dir appears to be a template dir. """ class MissingProjectDir(CookiecutterException): """ Raised during cleanup when remove_repo() can't find a generated project directory inside of a repo. """ class ConfigDoesNotExistException(CookiecutterException): """ Raised when get_config() is passed a path to a config file, but no file is found at that path. """ class InvalidConfiguration(CookiecutterException): """ Raised if the global configuration file is not valid YAML or is badly contructed. """ class UnknownRepoType(CookiecutterException): """ Raised if a repo's type cannot be determined. """ class VCSNotInstalled(CookiecutterException): """ Raised if the version control system (git or hg) is not installed. """ class ContextDecodingException(CookiecutterException): """ Raised when a project's JSON context file can not be decoded. """ class OutputDirExistsException(CookiecutterException): """ Raised when the output directory of the project exists already. """ 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 """ cookiecutter-1.3.0/cookiecutter/find.py000077500000000000000000000021041262047200400202020ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.find ----------------- Functions for finding Cookiecutter templates and other components. """ import logging import os from .exceptions import NonTemplatedInputDirException def find_template(repo_dir): """ Determines which child directory of `repo_dir` is the project template. :param repo_dir: Local directory of newly cloned repo. :returns project_template: Relative path to project template. """ logging.debug('Searching {0} for the project template.'.format(repo_dir)) repo_dir_contents = os.listdir(repo_dir) project_template = None for item in repo_dir_contents: if 'cookiecutter' in item and '{{' in item and '}}' in item: project_template = item break if project_template: project_template = os.path.join(repo_dir, project_template) logging.debug( 'The project template appears to be {0}'.format(project_template) ) return project_template else: raise NonTemplatedInputDirException cookiecutter-1.3.0/cookiecutter/generate.py000077500000000000000000000273101262047200400210620ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.generate --------------------- Functions for generating a project from a project template. """ from __future__ import unicode_literals from collections import OrderedDict import fnmatch import io import json import logging import os import shutil from jinja2 import FileSystemLoader, Template from jinja2.environment import Environment from jinja2.exceptions import TemplateSyntaxError 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, rmtree from .hooks import run_hook def copy_without_render(path, context): """ Returns True if `path` matches some pattern in the `_copy_without_render` context setting. :param path: A file-system path referring to a file or dir that should be rendered or just copied. :param context: cookiecutter context. """ try: for dont_render in context['cookiecutter']['_copy_without_render']: if fnmatch.fnmatch(path, dont_render): return True except KeyError: return False return False def apply_overwrites_to_context(context, overwrite_context): """Modify the given context in place based on the overwrite_context.""" for variable, overwrite in overwrite_context.items(): if variable not in context: # Do not include variables which are not used in the template continue context_value = context[variable] if isinstance(context_value, list): # We are dealing with a choice variable if overwrite in context_value: # This overwrite is actually valid for the given context # Let's set it as default (by definition first item in list) # see ``cookiecutter.prompt.prompt_choice_for_config`` context_value.remove(overwrite) context_value.insert(0, overwrite) else: # Simply overwrite the value for this variable context[variable] = overwrite def generate_context(context_file='cookiecutter.json', default_context=None, extra_context=None): """ Generates the context for a Cookiecutter project template. Loads the JSON file as a Python object, with key being the JSON filename. :param context_file: JSON file containing key/value pairs for populating the cookiecutter's variables. :param default_context: Dictionary containing config to take into account. :param extra_context: Dictionary containing configuration overrides """ context = {} file_handle = open(context_file) try: obj = json.load(file_handle, object_pairs_hook=OrderedDict) except ValueError as e: # JSON decoding error. Let's throw a new exception that is more # friendly for the developer or user. full_fpath = os.path.abspath(context_file) json_exc_message = str(e) our_exc_message = ( 'JSON decoding error while loading "{0}". Decoding' ' error details: "{1}"'.format(full_fpath, json_exc_message)) raise ContextDecodingException(our_exc_message) # Add the Python object to the context dictionary file_name = os.path.split(context_file)[1] file_stem = file_name.split('.')[0] context[file_stem] = obj # Overwrite context variable defaults with the default context from the # user's global config, if available if default_context: apply_overwrites_to_context(obj, default_context) if extra_context: apply_overwrites_to_context(obj, extra_context) logging.debug('Context generated is {0}'.format(context)) return context def generate_file(project_dir, infile, context, env): """ 1. Render the filename of infile as the name of outfile. 2. Deal with infile appropriately: a. If infile is a binary file, copy it over without rendering. b. If infile is a text file, render its contents and write the rendered infile to outfile. Precondition: When calling `generate_file()`, the root template dir must be the current working directory. Using `utils.work_in()` is the recommended way to perform this directory change. :param project_dir: Absolute path to the resulting generated project. :param infile: Input file to generate the file from. Relative to the root template dir. :param context: Dict for populating the cookiecutter's variables. :param env: Jinja2 template execution environment. """ logging.debug('Generating file {0}'.format(infile)) # Render the path to the output file (not including the root project dir) outfile_tmpl = Template(infile) outfile = os.path.join(project_dir, outfile_tmpl.render(**context)) file_name_is_empty = os.path.isdir(outfile) if file_name_is_empty: logging.debug('The resulting file name is empty: {0}'.format(outfile)) return logging.debug('outfile is {0}'.format(outfile)) # Just copy over binary files. Don't render. logging.debug("Check {0} to see if it's a binary".format(infile)) if is_binary(infile): logging.debug('Copying binary {0} to {1} without rendering' .format(infile, outfile)) shutil.copyfile(infile, outfile) else: # Force fwd slashes on Windows for get_template # This is a by-design Jinja issue infile_fwd_slashes = infile.replace(os.path.sep, '/') # Render the file try: tmpl = env.get_template(infile_fwd_slashes) except TemplateSyntaxError as exception: # Disable translated so that printed exception contains verbose # information about syntax error location exception.translated = False raise rendered_file = tmpl.render(**context) logging.debug('Writing {0}'.format(outfile)) with io.open(outfile, 'w', encoding='utf-8') as fh: fh.write(rendered_file) # Apply file permissions to output file shutil.copymode(infile, outfile) def render_and_create_dir(dirname, context, output_dir, overwrite_if_exists=False): """ Renders the name of a directory, creates the directory, and returns its path. """ name_tmpl = Template(dirname) rendered_dirname = name_tmpl.render(**context) logging.debug('Rendered dir {0} must exist in output_dir {1}'.format( rendered_dirname, output_dir )) dir_to_create = os.path.normpath( os.path.join(output_dir, rendered_dirname) ) output_dir_exists = os.path.exists(dir_to_create) if overwrite_if_exists: if output_dir_exists: logging.debug('Output directory {} already exists,' 'overwriting it'.format(dir_to_create)) else: if output_dir_exists: msg = 'Error: "{}" directory already exists'.format(dir_to_create) raise OutputDirExistsException(msg) make_sure_path_exists(dir_to_create) return dir_to_create def ensure_dir_is_templated(dirname): """ Ensures that dirname is a templated directory name. """ if '{{' in dirname and '}}' in dirname: return True else: 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): """ Renders the templates and saves them to files. :param repo_dir: Project template input directory. :param context: Dict for populating the template's variables. :param output_dir: Where to output the generated project dir into. :param overwrite_if_exists: Overwrite the contents of the output directory if it exists """ template_dir = find_template(repo_dir) logging.debug('Generating project from {0}...'.format(template_dir)) context = context or {} unrendered_dir = os.path.split(template_dir)[1] ensure_dir_is_templated(unrendered_dir) project_dir = render_and_create_dir(unrendered_dir, context, output_dir, overwrite_if_exists) # We want the Jinja path and the OS paths to match. Consequently, we'll: # + CD to the template folder # + Set Jinja's path to '.' # # In order to build our files to the correct folder(s), we'll use an # absolute path for the target folder (project_dir) project_dir = os.path.abspath(project_dir) logging.debug('project_dir is {0}'.format(project_dir)) _run_hook_from_repo_dir(repo_dir, 'pre_gen_project', project_dir, context) with work_in(template_dir): env = Environment(keep_trailing_newline=True) env.loader = FileSystemLoader('.') for root, dirs, files in os.walk('.'): # We must separate the two types of dirs into different lists. # The reason is that we don't want ``os.walk`` to go through the # unrendered directories, since they will just be copied. copy_dirs = [] render_dirs = [] for d in dirs: d_ = os.path.normpath(os.path.join(root, d)) # We check the full path, because that's how it can be # specified in the ``_copy_without_render`` setting, but # we store just the dir name if copy_without_render(d_, context): copy_dirs.append(d) else: render_dirs.append(d) for copy_dir in copy_dirs: indir = os.path.normpath(os.path.join(root, copy_dir)) outdir = os.path.normpath(os.path.join(project_dir, indir)) logging.debug( 'Copying dir {0} to {1} without rendering' ''.format(indir, outdir) ) shutil.copytree(indir, outdir) # We mutate ``dirs``, because we only want to go through these dirs # recursively dirs[:] = render_dirs for d in dirs: unrendered_dir = os.path.join(project_dir, root, d) render_and_create_dir(unrendered_dir, context, output_dir, overwrite_if_exists) for f in files: infile = os.path.normpath(os.path.join(root, f)) if copy_without_render(infile, context): outfile_tmpl = Template(infile) outfile_rendered = outfile_tmpl.render(**context) outfile = os.path.join(project_dir, outfile_rendered) logging.debug( 'Copying file {0} to {1} without rendering' ''.format(infile, outfile) ) shutil.copyfile(infile, outfile) shutil.copymode(infile, outfile) continue logging.debug('f is {0}'.format(f)) generate_file(project_dir, infile, context, env) _run_hook_from_repo_dir(repo_dir, 'post_gen_project', project_dir, context) return project_dir cookiecutter-1.3.0/cookiecutter/hooks.py000077500000000000000000000060331262047200400204120ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.hooks ------------------ Functions for discovering and executing various cookiecutter hooks. """ import io import logging import os import subprocess import sys 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(): """ Must be called with the project template as the current working directory. Returns a dict of all hook scripts provided. Dict's key will be the hook/script's name, without extension, while values will be the absolute path to the script. Missing scripts will not be included in the returned dict. """ hooks_dir = 'hooks' r = {} logging.debug('hooks_dir is {0}'.format(hooks_dir)) if not os.path.isdir(hooks_dir): logging.debug('No hooks/ dir in template_dir') return r for f in os.listdir(hooks_dir): basename = os.path.splitext(os.path.basename(f))[0] if basename in _HOOKS: r[basename] = os.path.abspath(os.path.join(hooks_dir, f)) return r def run_script(script_path, cwd='.'): """ Executes a script from a working directory. :param script_path: Absolute path to the script to run. :param cwd: The directory to run the script from. """ run_thru_shell = sys.platform.startswith('win') if script_path.endswith('.py'): script_command = [sys.executable, script_path] else: script_command = [script_path] utils.make_executable(script_path) proc = subprocess.Popen( script_command, shell=run_thru_shell, cwd=cwd ) 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): """ Executes a script after rendering with it Jinja. :param script_path: Absolute path to the script to run. :param cwd: The directory to run the script from. :param context: Cookiecutter project template context. """ _, extension = os.path.splitext(script_path) contents = io.open(script_path, 'r', encoding='utf-8').read() with tempfile.NamedTemporaryFile( delete=False, mode='wb', suffix=extension ) as temp: output = Template(contents).render(**context) temp.write(output.encode('utf-8')) run_script(temp.name, cwd) def run_hook(hook_name, project_dir, context): """ Try to find and execute a hook from the specified project directory. :param hook_name: The hook to execute. :param project_dir: The directory to execute the script from. :param context: Cookiecutter project context. """ script = find_hooks().get(hook_name) if script is None: logging.debug('No hooks found') return run_script_with_context(script, project_dir, context) cookiecutter-1.3.0/cookiecutter/main.py000077500000000000000000000104061262047200400202120ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.main ----------------- Main entry point for the `cookiecutter` command. The code in this module is also a good example of how to use Cookiecutter as a library rather than a script. """ from __future__ import unicode_literals import logging import os import re 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 from .vcs import clone from .replay import dump, load logger = logging.getLogger(__name__) builtin_abbreviations = { 'gh': 'https://github.com/{0}.git', 'bb': 'https://bitbucket.org/{0}', } REPO_REGEX = """ ( ((git|ssh|https|http):(//)?) # something like git:// ssh:// etc. | # or (\w+@[\w\.]+) # something like user@... ) .* """ def is_repo_url(value): """Return True if value is a repository URL.""" return bool(re.match(REPO_REGEX, value, re.VERBOSE)) def expand_abbreviations(template, config_dict): """ Expand abbreviations in a template name. :param template: The project template name. :param config_dict: The user config, which will contain abbreviation definitions. """ abbreviations = builtin_abbreviations.copy() abbreviations.update(config_dict.get('abbreviations', {})) if template in abbreviations: return abbreviations[template] # Split on colon. If there is no colon, rest will be empty # and prefix will be the whole template prefix, sep, rest = template.partition(':') if prefix in abbreviations: return abbreviations[prefix].format(rest) return template def cookiecutter( template, checkout=None, no_input=False, extra_context=None, replay=False, overwrite_if_exists=False, output_dir='.', config_file=USER_CONFIG_PATH): """ API equivalent to using Cookiecutter at the command line. :param template: A directory containing a project template directory, or a URL to a git repository. :param checkout: The branch, tag or commit ID to checkout after clone. :param no_input: Prompt the user at command line for manual configuration? :param extra_context: A dictionary of context that overrides default and user configuration. :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 = ( "You can not use both replay and no_input or extra_context " "at the same time." ) raise InvalidModeException(err_msg) # 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_file=config_file) template = expand_abbreviations(template, config_dict) if is_repo_url(template): repo_dir = clone( repo_url=template, checkout=checkout, clone_to_dir=config_dict['cookiecutters_dir'], no_input=no_input ) else: # If it's a local repo, no need to clone or copy to your # cookiecutters_dir repo_dir = template template_name = os.path.basename(template) if replay: context = load(template_name) else: context_file = os.path.join(repo_dir, 'cookiecutter.json') logging.debug('context_file is {0}'.format(context_file)) context = generate_context( context_file=context_file, default_context=config_dict['default_context'], extra_context=extra_context, ) # prompt the user to manually configure at the command line. # except when 'no-input' flag is set context['cookiecutter'] = prompt_for_config(context, no_input) dump(template_name, context) # Create project from local context and project template. return generate_files( repo_dir=repo_dir, context=context, overwrite_if_exists=overwrite_if_exists, output_dir=output_dir ) cookiecutter-1.3.0/cookiecutter/prompt.py000077500000000000000000000075571262047200400206240ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.prompt --------------------- Functions for prompting the user for project info. """ from collections import OrderedDict import click from past.builtins import basestring from future.utils import iteritems from jinja2.environment import Environment def read_user_variable(var_name, default_value): """Prompt the user for the given variable and return the entered value or the given default. :param str var_name: Variable of the context to query the user :param default_value: Value that will be returned if no input happens """ # Please see http://click.pocoo.org/4/api/#click.prompt return click.prompt(var_name, default=default_value) def read_user_yes_no(question, default_value): """Prompt the user to reply with 'yes' or 'no' (or equivalent values). Note: Possible choices are 'true', '1', 'yes', 'y' or 'false', '0', 'no', 'n' :param str question: Question to the user :param default_value: Value that will be returned if no input happens """ # Please see http://click.pocoo.org/4/api/#click.prompt return click.prompt( question, default=default_value, type=click.BOOL ) def read_user_choice(var_name, options): """Prompt the user to choose from several options for the given variable. The first item will be returned if no input happens. :param str var_name: Variable as specified in the context :param list options: Sequence of options that are available to select from :return: Exactly one item of ``options`` that has been chosen by the user """ # Please see http://click.pocoo.org/4/api/#click.prompt if not isinstance(options, list): raise TypeError if not options: raise ValueError choice_map = OrderedDict( (u'{}'.format(i), value) for i, value in enumerate(options, 1) ) choices = choice_map.keys() default = u'1' choice_lines = [u'{} - {}'.format(*c) for c in choice_map.items()] prompt = u'\n'.join(( u'Select {}:'.format(var_name), u'\n'.join(choice_lines), u'Choose from {}'.format(u', '.join(choices)) )) user_choice = click.prompt( prompt, type=click.Choice(choices), default=default ) return choice_map[user_choice] def render_variable(env, raw, cookiecutter_dict): if not isinstance(raw, basestring): raw = str(raw) template = env.from_string(raw) rendered_template = template.render(cookiecutter=cookiecutter_dict) return rendered_template def prompt_choice_for_config(cookiecutter_dict, env, key, options, no_input): """Prompt the user which option to choose from the given. Each of the possible choices is rendered beforehand. """ rendered_options = [ render_variable(env, raw, cookiecutter_dict) for raw in options ] if no_input: return rendered_options[0] return read_user_choice(key, rendered_options) def prompt_for_config(context, no_input=False): """ Prompts the user to enter new config, using context as a source for the field names and sample values. :param no_input: Prompt the user at command line for manual configuration? """ cookiecutter_dict = {} env = Environment() for key, raw in iteritems(context[u'cookiecutter']): if key.startswith(u'_'): cookiecutter_dict[key] = raw continue if isinstance(raw, list): # We are dealing with a choice variable val = prompt_choice_for_config( cookiecutter_dict, env, key, raw, no_input ) else: # We are dealing with a regular variable val = render_variable(env, raw, cookiecutter_dict) if not no_input: val = read_user_variable(key, val) cookiecutter_dict[key] = val return cookiecutter_dict cookiecutter-1.3.0/cookiecutter/replay.py000066400000000000000000000030341262047200400205560ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ cookiecutter.replay ------------------- """ from __future__ import unicode_literals import json import os from past.builtins import basestring from .config import get_user_config from .utils import make_sure_path_exists def get_file_name(replay_dir, template_name): file_name = '{}.json'.format(template_name) return os.path.join(replay_dir, file_name) def dump(template_name, context): if not isinstance(template_name, basestring): raise TypeError('Template name is required to be of type str') if not isinstance(context, dict): raise TypeError('Context is required to be of type dict') if 'cookiecutter' not in context: raise ValueError('Context is required to contain a cookiecutter key') replay_dir = get_user_config()['replay_dir'] if not make_sure_path_exists(replay_dir): raise IOError('Unable to create replay dir at {}'.format(replay_dir)) replay_file = get_file_name(replay_dir, template_name) with open(replay_file, 'w') as outfile: json.dump(context, outfile) def load(template_name): if not isinstance(template_name, basestring): raise TypeError('Template name is required to be of type str') replay_dir = get_user_config()['replay_dir'] replay_file = get_file_name(replay_dir, template_name) with open(replay_file, 'r') as infile: context = json.load(infile) if 'cookiecutter' not in context: raise ValueError('Context is required to contain a cookiecutter key') return context cookiecutter-1.3.0/cookiecutter/utils.py000077500000000000000000000031161262047200400204260ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.utils ------------------ Helper functions used throughout Cookiecutter. """ from __future__ import unicode_literals import contextlib import errno import logging import os import stat import shutil def force_delete(func, path, exc_info): """ Error handler for `shutil.rmtree()` equivalent to `rm -rf` Usage: `shutil.rmtree(path, onerror=force_delete)` From stackoverflow.com/questions/1889597 """ os.chmod(path, stat.S_IWRITE) func(path) def rmtree(path): """ Removes a directory and all its contents. Like rm -rf on Unix. :param path: A directory path. """ shutil.rmtree(path, onerror=force_delete) def make_sure_path_exists(path): """ Ensures that a directory exists. :param path: A directory path. """ logging.debug('Making sure path exists: {0}'.format(path)) try: os.makedirs(path) except OSError as exception: if exception.errno != errno.EEXIST: return False return True @contextlib.contextmanager def work_in(dirname=None): """ Context manager version of os.chdir. When exited, returns to the working directory prior to entering. """ curdir = os.getcwd() try: if dirname is not None: os.chdir(dirname) yield finally: os.chdir(curdir) def make_executable(script_path): """ Makes `script_path` executable :param script_path: The file to change """ status = os.stat(script_path) os.chmod(script_path, status.st_mode | stat.S_IEXEC) cookiecutter-1.3.0/cookiecutter/vcs.py000077500000000000000000000071171262047200400200660ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ cookiecutter.vcs ---------------- Helper functions for working with version control systems. """ from __future__ import unicode_literals import logging import os import subprocess import sys from whichcraft import which from .exceptions import UnknownRepoType, VCSNotInstalled from .prompt import read_user_yes_no from .utils import make_sure_path_exists, rmtree def prompt_and_delete_repo(repo_dir, no_input=False): """ Asks the user whether it's okay to delete the previously-cloned repo. If yes, deletes it. Otherwise, Cookiecutter exits. :param repo_dir: Directory of previously-cloned repo. :param no_input: Suppress prompt to delete repo and just delete it. """ # Suppress prompt if called via API if no_input: ok_to_delete = True else: question = ( "You've cloned {0} before. " 'Is it okay to delete and re-clone it?' ).format(repo_dir) ok_to_delete = read_user_yes_no(question, 'yes') if ok_to_delete: rmtree(repo_dir) else: sys.exit() def identify_repo(repo_url): """ Determines if `repo_url` should be treated as a URL to a git or hg repo. Repos can be identified prepeding "hg+" or "git+" to repo URL. :param repo_url: Repo URL of unknown type. :returns: ("git", repo_url), ("hg", repo_url), or None. """ repo_url_values = repo_url.split('+') if len(repo_url_values) == 2: repo_type = repo_url_values[0] if repo_type in ["git", "hg"]: return repo_type, repo_url_values[1] else: raise UnknownRepoType else: if "git" in repo_url: return "git", repo_url elif "bitbucket" in repo_url: return "hg", repo_url else: raise UnknownRepoType def is_vcs_installed(repo_type): """ Check if the version control system for a repo type is installed. :param repo_type: """ return bool(which(repo_type)) def clone(repo_url, checkout=None, clone_to_dir=".", no_input=False): """ Clone a repo to the current directory. :param repo_url: Repo URL of unknown type. :param checkout: The branch, tag or commit ID to checkout after clone. :param clone_to_dir: The directory to clone to. Defaults to the current directory. :param no_input: Suppress all user prompts when calling via API. """ # Ensure that clone_to_dir exists clone_to_dir = os.path.expanduser(clone_to_dir) make_sure_path_exists(clone_to_dir) # identify the repo_type repo_type, repo_url = identify_repo(repo_url) # check that the appropriate VCS for the repo_type is installed if not is_vcs_installed(repo_type): 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, tail.rsplit('.git')[0])) elif repo_type == 'hg': repo_dir = os.path.normpath(os.path.join(clone_to_dir, tail)) logging.debug('repo_dir is {0}'.format(repo_dir)) if os.path.isdir(repo_dir): prompt_and_delete_repo(repo_dir, no_input=no_input) if repo_type in ['git', 'hg']: subprocess.check_call([repo_type, 'clone', repo_url], cwd=clone_to_dir) if checkout is not None: subprocess.check_call([repo_type, 'checkout', checkout], cwd=repo_dir) return repo_dir cookiecutter-1.3.0/cookiecutter_medium.png000066400000000000000000000207371262047200400207670ustar00rootroot00000000000000PNG  IHDR_SWsBIT|d pHYs(RtEXtSoftwarewww.inkscape.org< IDATxwU{sCBB- /@@i..IΜs~;3s~W nTʅ0lXP^^^/K~WAAR.JR.|88 $gK{ -  A~E俔ј (+@qhŽ70(SWg%Yβ Bh2Srap2p02A1Z,y # H]WʅA7ЎjN70X-  4"U%^).Om\\],FAjIEWʅ)=ΣŒrނ B5*t>81NۃerY mAbJR.nAzL>W, DAQra;6`؝?K̼A6q; Axisag`" k%^)\AȄ-W ==ƣap(EɃ9Œxނ #%^)סC1g>|0V|0~UdX|X[AaajN !xO9fnVfê= c\=A 'W9^FH>|LGKy ! "+gÞpqtxIszSB J<ƾU2d=:$z8R* Yʜ^)OD`j^ }UZBw¨ ` AhăT&Ll=A 3!ΙJ©T;W?F ;MWA5G_p/p(pA!oZ*=6a,dLæme+َ-Jk #@z=yӌ@MQB~ ;g!)o?^z=#!W඲ ]m~iˀ.c>xqD88=eÚ|xxxq%I [Y{cZYg>p<0=qޖv;TJFA?C˃}`zRdo+ !q?[ K{G% uxq_VgמkzDi+ѿk|x=}A'[e"`$[Y`}M ^>}5}|d }(pTFc8RvN43Fme ~G \yE /be5oތ؍>.B+0~\aBIYI2v\^#K+le}8 38q e xh۞խrg+HV}Y= r&ao)"='z?_TleMDGb0_ L.v\oa? i+aj4B(zO[Y7D֐}0P^=k+wD݆69v?j#~j?VADmd+k /ab+:Ƿ!mٴ9۶8^uЈ]memek{) ?"6 e<^rxV/meսkW>̌ǶvZ ;L5L \w^g|V!6 9zj+tb+kPND ~g+`?2k4h3m*w!iƥ=B+q[Y& ǖR7 <ΨNڌ>g/?SJ< @j7?~\ ׺'Iu2J+愐c ڑ.m Ŷ1iL6>T9H[YbyA}մdia6NQ)$cTcx8hgj\ă"'R"N^qWS(WoOC'!/t[Y{?#7ZyLmɒleM6hw=K7K&:.z6U:利 GM=lem8Od,fJ';q[^w ժpW#S~ec;xW౫ t8m!9lVV֓3ɶN@;T[YO Kg4Fhi̔GWﯴӳܜ][(MˌJ'~9Ml/`O󿣕z;(ŋRÀ[nSle+py"5dZR_1nmp8´GeMY$وiy*RQٳc JR. !0IT ٬&ޫ-9Պsa`y5ptZASP0<5DB4|Xai:<::@9Of Ϝ+*zӻ?]ۍW'&W#zh3 *HV45&g}FbF;w% m,MO6ӓΌ2w0SFph\ 3^w\/+XkBz)=gM{8x;1+Bd;5KЉ3VJe+kW͜@<'hqV;q Kdqov-^U3֪ߊp~%P }BSnq:<Ϣ>8 #i|>K58 {-{U*#3ͅ}I`LГIYr@`:pX+p0WFG+އ[kV}| 'xL[Y_0c :g-?=%ߤ+(U>$^9.< 7D8:q[\/kyʊq\/ OݔP_6L ^Gȼp\b+>)qqB}+:po&q#Fr\/G[cmu&K/pDǂFIޟIKyl wvCV`?*5/F517rD8;L-Eg7:3DleC+(#_v|#OsfʺUAH:3J΃5Je@ 5n+kk̾[;>[&D|Z^3B3twVݵ֫/*j9'M :$DOzaNNemIᰇAgÄqh( (J<8)@&#Y~f''=szz:)x(YBQ uZ@v Bڼtâ%l.ZTy<@ L7;hB'l,tvPv1RAq "z&NJI)U V$4iISAȁi]$A%>>QK0jT<]'*ĥ#yޠoUcESzOFl[jՔ#zi\y1V49a69Gʚ=뽑4PxIL@W]ӭ_F%J4R|x͠MFԦlgY*,Kd8p]svuI=a`qn/x_$cQsMJ̜>k.n5!&y]&j>KqD9Cu+Vdމ,2w\͛0MhC| [=$e+âK V/5N7i3QLJ">NJTDSz%m Aq\/ºX' ˁA $8QsoD'gZC"+;uV0O䁟VɊeXQ%aN9U.k%zQS .&?x̒g]R AbVFQ]Q\8=;gĤ߬ckӡȚT@Tc+4sݺ@k0=RAd k fV8E"+EnbKkgRڮvudb05jW+kle}$qf~QQ2a[*>]/6ի;P  lcfȻхBdFjL%&plY5z li#De bZQ}/jPLGŏ*iiuLVVMώQE#>$P&&S䷧+paNcE,&m*ނTė+ [m#V~i33KaՠI$Yɚgӂ=M^W]/a9_&Y_βp!I %l9kᥕrbɟBp,sviqE b4aˉVl[Y{3B$d5e1E*W[Y;9MV^)lm}J&ʺ S)RSiP=JhzB(^Zd,u ʆFM|'ɶ$z[58h1E2u/)8/v55pt҂BLD[BKz성va0==. &m:le%`"i&0ap)p4kj1z9Ir~N<2hW57{q95ٷo?RjV:p Εo(NZ"٬  j+ pY}ʷ%pkeq\oyDO4 un.}DcHk'jfӈ~_:z ߷%$T$/E :u"&ڻIW59Qp}_nor<#֌!:뚛Zkm+nv[Yʚi+kV v\u{[Y3leo(k5'a_/4&H›xc`'`vAw{$Ic.īLj"(7x\~V;8;F?2&sڊ'xU(j?q|Aش e9VV=7C1 3}Q%icvz;^a|]Ⲹm2˪/Hf!\vu4p\Q,^AYlL! > Rѥm&B*{o谳l|@D_XqW:w#pCF&;q?ץ/RSQ b8mbҊ|Ie-BGd#H#aМM"w"]cl[YgݤK\ES ӌV^ǑYbpSi ӂ+׻%׻sOksme\"5#hZ;q]~leK%:5g\9=ЉZ>,&f8U+񻀕R.H ;6ط֬B>( [RϺr@yqfS2UGp Xڌ}3؟?L)(isZ &Ȼ5 @;0j [=I]>kI<vv\cEU$ \,O;J^B;pzYM=AWYٿu8| :&$_!C _8w:~5c)o^ AOkdi^)˰2E{nD;? C{. ^ 9P>7E+A;EޏE/$^ZV;g+V>IюV8J+O~ $tf,E_'ﱧ`keow*-Qতk2SpCO7:Xxj; 1{𓨿A3c2z0pq ct6E7=U`tENۅq;zK%P)Jy/ JKBl8eBˇr^dֈ(H볶7SvB͇X:楝8%T9ZyAO^QB\[m5Rߓ"ASx%5'JЧ  dIݕx/r~t,PKy ! LZ9[NtﻁTY yTKS%_E. B2g!Z1R.dULAJX}+W XhRƜ+E YԱJ:FJᆵ[A ئXH' p˜(7e-"G3> a*蚟ٰ>0Y 8@?.JѶX ]U9κ'iŒLFA"*qb p] $`uUE~/e.)I}AA"8&IAaaFh_Y,Z ЀЎm (˖ë p>pj_h ӕ8Œrs'&M1.X=iQ BRm7 ҍ&|Q2qCĉMAțJX_H?S:/_`bɟ'AAI"JXGSȱh S_8X5m.HPIDATEA$c[#*¿wy `fڡf6GGYt璑NA!%K\8)A|%,Y:&nM!.rCnu  dMJR.t \l`u}*;UmEX-" H]R)d2h4f?-FAZR. ObAgX&  &P `` ';?K 8*j*PS6Mѵs .6JJP͚6U xbϮ  dxG7IENDB`cookiecutter-1.3.0/docs/000077500000000000000000000000001262047200400151405ustar00rootroot00000000000000cookiecutter-1.3.0/docs/Makefile000066400000000000000000000152021262047200400166000ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(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/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/cookiecutter.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/cookiecutter.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/cookiecutter" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/cookiecutter" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." cookiecutter-1.3.0/docs/__init__.py000066400000000000000000000000001262047200400172370ustar00rootroot00000000000000cookiecutter-1.3.0/docs/advanced_usage.rst000066400000000000000000000230121262047200400206210ustar00rootroot00000000000000============== Advanced Usage ============== Using Pre/Post-Generate Hooks (0.7.0+) -------------------------------------- You can have Python or Shell scripts that run before and/or after your project is generated. Put them in `hooks/` like this:: cookiecutter-something/ ├── {{cookiecutter.repo_name}}/ ├── hooks │ ├── pre_gen_project.py │ └── post_gen_project.py └── cookiecutter.json Shell scripts work similarly:: cookiecutter-something/ ├── {{cookiecutter.repo_name}}/ ├── hooks │ ├── pre_gen_project.sh │ └── post_gen_project.sh └── cookiecutter.json It shouldn't be too hard to extend Cookiecutter to work with other types of scripts too. Pull requests are welcome. For portability, you should use Python scripts (with extension `.py`) for your 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 `_), 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 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 default_context: full_name: "Audrey Roy" email: "audreyr@gmail.com" github_username: "audreyr" cookiecutters_dir: "/home/audreyr/my-custom-cookiecutters-dir/" replay_dir: "/home/audreyr/my-custom-replay-dir/" abbreviations: pp: https://github.com/audreyr/cookiecutter-pypackage.git gh: https://github.com/{0}.git bb: https://bitbucket.org/{0} Possible settings are: * default_context: A list of key/value pairs that you want injected as context whenever you generate a project with Cookiecutter. These values are treated like the defaults in `cookiecutter.json`, upon generation of any project. * cookiecutters_dir: Directory where your cookiecutters are cloned to when you use Cookiecutter with a repo argument. * replay_dir: Directory where Cookiecutter dumps context data to, which you can fetch later on when using the `replay feature`_. * abbreviations: A list of abbreviations for cookiecutters. Abbreviations can be simple aliases for a repo name, or can be used as a prefix, in the form `abbr:suffix`. Any suffix will be inserted into the expansion in place of the text `{0}`, using standard Python string formatting. With the above aliases, you could use the `cookiecutter-pypackage` template simply by saying `cookiecutter pp`, or `cookiecutter gh:audreyr/cookiecutter-pypackage`. The `gh` (github) and `bb` (bitbucket) abbreviations shown above are actually built in, and can be used without defining them yourself. Calling Cookiecutter Functions From Python ------------------------------------------ You can use Cookiecutter from Python:: from cookiecutter.main import cookiecutter # Create project from the cookiecutter-pypackage/ template cookiecutter('cookiecutter-pypackage/') # Create project from the cookiecutter-pypackage.git repo template cookiecutter('https://github.com/audreyr/cookiecutter-pypackage.git') This is useful if, for example, you're writing a web framework and need to provide developers with a tool similar to `django-admin.py startproject` or `npm init`. Injecting Extra Context ----------------------- You can specify an `extra_context` dictionary that will override values from `cookiecutter.json` or `.cookiecutterrc`:: cookiecutter('cookiecutter-pypackage/', extra_context={'project_name': 'TheGreatest'}) Example: Injecting a Timestamp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a sample Python script that dynamically injects a timestamp value as a project is generated:: from cookiecutter.main import cookiecutter from datetime import datetime cookiecutter( 'cookiecutter-django', extra_context={'timestamp': datetime.utcnow().isoformat()} ) How this works: 1. The script uses `datetime` to get the current UTC time in ISO format. 2. To generate the project, `cookiecutter()` is called, passing the timestamp in as context via the `extra_context` dict. Suppressing Command-Line Prompts -------------------------------- To suppress the prompts asking for input, use `no_input`. Basic Example: Using the Defaults ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TODO: document `no_input`: * As command-line argument * As parameter of `cookiecutter()` TODO: document where context values come from in this example (`cookiecutter.json` and `.cookiecutterrc`) Advanced Example: Defaults + Extra Context ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you combine an `extra_context` dict with the `no_input` argument, you can programmatically create the project with a set list of context parameters and without any command line prompts:: cookiecutter('cookiecutter-pypackage/', no_input=True, extra_context={'project_name': 'TheGreatest'}) See the :ref:`API Reference ` for more details. Templates in Context Values -------------------------------- The values (but not the keys!) of `cookiecutter.json` are also Jinja2 templates. Values from user prompts are added to the context immediately, such that one context value can be derived from previous values. This approach can potentially save your user a lot of keystrokes by providing more sensible defaults. Basic Example: Templates in Context ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python packages show some patterns for their naming conventions: * a human-readable project name * a lowercase, dashed repository name * an importable, dash-less package name Here is a `cookiecuttter.json` with templated values for this pattern:: { "project_name": "My New Project", "repo_name": "{{ cookiecutter.project_name|lower|replace(' ', '-') }}", "pkg_name": "{{ cookiecutter.repo_name|replace('-', '') }}" } If the user takes the defaults, or uses `no_input`, the templated values will be: * `my-new-project` * `mynewproject` Or, if the user gives `Yet Another New Project`, the values will be: * `yet-another-new-project` * `yetanothernewproject` Copy without Render ------------------- *New in Cookiecutter 1.1* To avoid rendering directories and files of a cookiecutter mould, the `_copy_without_render` key can be used in the `cookiecutter.json`. The value of this key accepts a list of Unix shell-style wildcards:: { "repo_name": "sample", "_copy_without_render": [ "*.html", "*not_rendered_dir", "rendered_dir/not_rendered_file.ini" ] } .. _`replay feature`: Replay Project Generation ------------------------- *New in Cookiecutter 1.1* On invocation **Cookiecutter** dumps a json file to ``~/.cookiecutter_replay/`` which enables you to *replay* later on. In other words, it persists your **input** for a template and fetches it when you run the same template again. Example for a replay file (which was created via ``cookiecutter gh:hackebrot/cookiedozer``):: { "cookiecutter": { "app_class_name": "FooBarApp", "app_title": "Foo Bar", "email": "raphael@hackebrot.de", "full_name": "Raphael Pierzina", "github_username": "hackebrot", "kivy_version": "1.8.0", "repo_name": "foobar", "short_description": "A sleek slideshow app that supports swipe gestures.", "version": "0.1.0", "year": "2015" } } To fetch this context data without being prompted on the command line you can use either of the following methods. Pass the according option on the CLI:: cookiecutter --replay gh:hackebrot/cookiedozer Or use the Python API:: from cookiecutter.main import cookiecutter cookiecutter('gh:hackebrot/cookiedozer', replay=True) This feature is comes in handy if, for instance, you want to create a new project from an updated template. .. _command_line_options: Command Line Options -------------------- .. cc-command-line-options:: cookiecutter-1.3.0/docs/authors.rst000066400000000000000000000000341262047200400173540ustar00rootroot00000000000000.. include:: ../AUTHORS.rst cookiecutter-1.3.0/docs/ccext.py000066400000000000000000000017631262047200400166270ustar00rootroot00000000000000# -*- coding: utf-8 -*- from cookiecutter import cli import click from docutils import nodes from docutils.parsers import rst from docutils.statemachine import ViewList class CcCommandLineOptions(rst.Directive): def _format_option(self, option): return [ ".. _`%s`:" % option.name, "", ".. option:: " + ", ".join(option.opts), "", option.help, "" ] def process_actions(self): for option in cli.main.params: if isinstance(option, click.core.Option): for line in self._format_option(option): self.view_list.append(line, "") def run(self): node = nodes.paragraph() node.document = self.state.document self.view_list = ViewList() self.process_actions() self.state.nested_parse(self.view_list, 0, node) return [node] def setup(app): app.add_directive('cc-command-line-options', CcCommandLineOptions) cookiecutter-1.3.0/docs/conf.py000066400000000000000000000252331262047200400164440ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # cookiecutter documentation build configuration file, created by # sphinx-quickstart on Thu Jul 11 11:31:49 2013. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # For building docs in foreign environments where we don't have all our # dependencies (like readthedocs), mock out imports that cause sphinx to fail. # see: https://docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules import sys class Mock(object): def __init__(self, *args, **kwargs): pass def __call__(self, *args, **kwargs): return Mock() @classmethod def __getattr__(cls, name): if name in ('__file__', '__path__'): return '/dev/null' elif name[0] == name[0].upper(): mockType = type(name, (), {}) mockType.__module__ = __name__ return mockType else: return Mock() MOCK_MODULES = ['yaml'] for mod_name in MOCK_MODULES: sys.modules[mod_name] = Mock() # Add parent dir to path cwd = os.getcwd() parent = os.path.dirname(cwd) sys.path.append(parent) import cookiecutter # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'docs.ccext'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'cookiecutter' copyright = u'2013-2015, Audrey Roy' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = cookiecutter.__version__ # The full version, including alpha/beta/rc tags. release = cookiecutter.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. 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 # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'cookiecutterdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'cookiecutter.tex', u'cookiecutter Documentation', u'Audrey Roy', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'cookiecutter', u'cookiecutter Documentation', [u'Audrey Roy'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'cookiecutter', u'cookiecutter Documentation', u'Audrey Roy', 'cookiecutter', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'cookiecutter' epub_author = u'Audrey Roy' epub_publisher = u'Audrey Roy' epub_copyright = u'2013-2015, Audrey Roy' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. #epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. #epub_exclude_files = [] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True # Fix unsupported image types using the PIL. #epub_fix_images = False # Scale large images. #epub_max_image_width = 0 # If 'no', URL addresses will not be shown. #epub_show_urls = 'inline' # If false, no index is generated. #epub_use_index = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} cookiecutter-1.3.0/docs/contributing.rst000066400000000000000000000005041262047200400204000ustar00rootroot00000000000000============ Contributing ============ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. .. toctree:: :numbered: :maxdepth: 2 types_of_contributions contributor_setup contributor_guidelines contributor_testing core_committer_guide cookiecutter-1.3.0/docs/contributor_guidelines.rst000066400000000000000000000033771262047200400224660ustar00rootroot00000000000000Contributor Guidelines ----------------------- Pull Request Guidelines ~~~~~~~~~~~~~~~~~~~~~~~~ Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. 3. The pull request should work for Python 2.7, 3.3, 3.4, 3.5, and PyPy on Appveyor and Travis CI. 4. Check https://travis-ci.org/audreyr/cookiecutter/pull_requests and https://ci.appveyor.com/project/audreyr/cookiecutter/history to ensure the tests pass for all supported Python versions and platforms. Coding Standards ~~~~~~~~~~~~~~~~ * PEP8 * Functions over classes except in tests * Quotes via http://stackoverflow.com/a/56190/5549 * Use double quotes around strings that are used for interpolation or that are natural language messages * Use single quotes for small symbol-like strings (but break the rules if the strings contain quotes) * Use triple double quotes for docstrings and raw string literals for regular expressions even if they aren't needed. * Example: .. code-block:: python LIGHT_MESSAGES = { 'English': "There are %(number_of_lights)s lights.", 'Pirate': "Arr! Thar be %(number_of_lights)s lights." } def lights_message(language, number_of_lights): """Return a language-appropriate string reporting the light count.""" return LIGHT_MESSAGES[language] % locals() def is_pirate(message): """Return True if the given message sounds piratical.""" return re.search(r"(?i)(arr|avast|yohoho)!", message) is not None * Write new code in Python 3. cookiecutter-1.3.0/docs/contributor_setup.rst000066400000000000000000000024241262047200400214660ustar00rootroot00000000000000Setting Up the Code for Local Development ----------------------------------------- Here's how to set up `cookiecutter` for local development. 1. Fork the `cookiecutter` repo on GitHub. 2. Clone your fork locally:: $ git clone git@github.com:your_name_here/cookiecutter.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: $ mkvirtualenv cookiecutter $ cd cookiecutter/ $ python setup.py develop 4. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. 5. When you're done making changes, check that your changes pass the tests and flake8:: $ pip install tox $ tox Please note that tox runs flake8 automatically, since we have a test environment for it. If you feel like running only the flake8 environment, please use the following command:: $ tox -e flake8 6. Commit your changes and push your branch to GitHub:: $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature 7. Check that the test coverage hasn't dropped:: $ tox -e cov-report 8. Submit a pull request through the GitHub website. cookiecutter-1.3.0/docs/contributor_testing.rst000066400000000000000000000024611262047200400220040ustar00rootroot00000000000000Testing with tox ---------------- Tox uses py.test under the hood, hence it supports the same syntax for selecting tests. For further information please consult the `pytest usage docs`_. To run a particular test class with tox:: $ tox -e py '-k TestFindHooks' To run some tests with names matching a string expression:: $ tox -e py '-k generate' Will run all tests matching "generate", test_generate_files for example. To run just one method:: $ tox -e py '-k "TestFindHooks and test_find_hook"' To run all tests using various versions of python in virtualenvs defined in tox.ini, just run tox.:: $ tox This configuration file setup the pytest-cov plugin and it is an additional dependency. It generate a coverage report after the tests. It is possible to tests with some versions of python, to do this the command is:: $ tox -e py27,py34,pypy Will run py.test with the python2.7, python3.4 and pypy interpreters, for example. Troubleshooting for Contributors --------------------------------- Python 3.3 tests fail locally ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Try upgrading Tox to the latest version. I noticed that they were failing locally with Tox 1.5 but succeeding when I upgraded to Tox 1.7.1. .. _`pytest usage docs`: https://pytest.org/latest/usage.html#specifying-tests-selecting-tests cookiecutter-1.3.0/docs/cookiecutter.rst000066400000000000000000000034171262047200400203770ustar00rootroot00000000000000cookiecutter package ==================== Submodules ---------- cookiecutter.cli module ----------------------- .. automodule:: cookiecutter.cli :members: :undoc-members: :show-inheritance: cookiecutter.config module -------------------------- .. automodule:: cookiecutter.config :members: :undoc-members: :show-inheritance: cookiecutter.exceptions module ------------------------------ .. automodule:: cookiecutter.exceptions :members: :undoc-members: :show-inheritance: cookiecutter.find module ------------------------ .. automodule:: cookiecutter.find :members: :undoc-members: :show-inheritance: cookiecutter.generate module ---------------------------- .. automodule:: cookiecutter.generate :members: :undoc-members: :show-inheritance: cookiecutter.hooks module ------------------------- .. automodule:: cookiecutter.hooks :members: :undoc-members: :show-inheritance: cookiecutter.main module ------------------------ .. automodule:: cookiecutter.main :members: :undoc-members: :show-inheritance: cookiecutter.prompt module -------------------------- .. automodule:: cookiecutter.prompt :members: :undoc-members: :show-inheritance: cookiecutter.replay module -------------------------- .. automodule:: cookiecutter.replay :members: :undoc-members: :show-inheritance: cookiecutter.utils module ------------------------- .. automodule:: cookiecutter.utils :members: :undoc-members: :show-inheritance: cookiecutter.vcs module ----------------------- .. automodule:: cookiecutter.vcs :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: cookiecutter :members: :undoc-members: :show-inheritance: cookiecutter-1.3.0/docs/core_committer_guide.rst000066400000000000000000000174451262047200400220750ustar00rootroot00000000000000Core Committer Guide ==================== Vision and Scope ----------------- Core committers, use this section to: * Guide your instinct and decisions as a core committer * Limit the codebase from growing infinitely Command-Line Accessible ~~~~~~~~~~~~~~~~~~~~~~~ * Provides a command-line utility that creates projects from cookiecutters * Extremely easy to use without having to think too hard * Flexible for more complex use via optional arguments API Accessible ~~~~~~~~~~~~~~ * Entirely function-based and stateless (Class-free by intentional design) * Usable in pieces for developers of template generation tools Being Jinja2-specific ~~~~~~~~~~~~~~~~~~~~~ * Sets a standard baseline for project template creators, facilitating reuse * Minimizes the learning curve for those who already use Flask or Django * Minimizes scope of Cookiecutter codebase Extensible ~~~~~~~~~~ Being extendable by people with different ideas for Jinja2-based project template tools. * Entirely function-based * Aim for statelessness * Lets anyone write more opinionated tools Freedom for Cookiecutter users to build and extend. * No officially-maintained cookiecutter templates, only ones by individuals * Commercial project-friendly licensing, allowing for private cookiecutters and private Cookiecutter-based tools Fast and Focused ~~~~~~~~~~~~~~~~ Cookiecutter is designed to do one thing, and do that one thing very well. * Cover the use cases that the core committers need, and as little as possible beyond that :) * Generates project templates from the command-line or API, nothing more * Minimize internal line of code (LOC) count * Ultra-fast project generation for high performance downstream tools Inclusive ~~~~~~~~~ * Cross-platform and cross-version support are more important than features/functionality * Fixing Windows bugs even if it's a pain, to allow for use by more beginner coders Stable ~~~~~~ * Aim for 100% test coverage and covering corner cases * No pull requests will be accepted that drop test coverage on any platform, including Windows * Conservative decisions patterned after CPython's conservative decisions with stability in mind * Stable APIs that tool builders can rely on * New features require a +1 from 3 core committers VCS-Hosted Templates ~~~~~~~~~~~~~~~~~~~~~ Cookiecutter project templates are intentionally hosted VCS repos as-is. * They are easily forkable * It's easy for users to browse forks and files * They are searchable via standard Github/Bitbucket/other search interface * Minimizes the need for packaging-related cruft files * Easy to create a public project template and host it for free * Easy to collaborate Process: Pull Requests ------------------------ If a pull request is untriaged: * Look at the roadmap * Set it for the milestone where it makes the most sense * Add it to the roadmap How to prioritize pull requests, from most to least important: #. Fixes for broken tests. Broken means broken on any supported platform or Python version. #. Extra tests to cover corner cases. #. Minor edits to docs. #. Bug fixes. #. Major edits to docs. #. Features. Ensure that each pull request meets all requirements in this checklist: https://gist.github.com/audreyr/4feef90445b9680475f2 Process: Issues ---------------- If an issue is a bug that needs an urgent fix, mark it for the next patch release. Then either fix it or mark as please-help. For other issues: encourage friendly discussion, moderate debate, offer your thoughts. New features require a +1 from 2 other core committers (besides yourself). Process: Roadmap ----------------- The roadmap is https://github.com/audreyr/cookiecutter/milestones?direction=desc&sort=due_date&state=open Due dates are flexible. Core committers can change them as needed. Note that GitHub sort on them is buggy. How to number milestones: * Follow semantic versioning. See http://semver.org Milestone size: * If a milestone contains too much, move some to the next milestone. * Err on the side of more frequent patch releases. Process: Pull Request merging and HISTORY.rst maintenance --------------------------------------------------------- If you merge a pull request, you're responsible for updating `AUTHORS.rst` and `HISTORY.rst` When you're processing the first change after a release, create boilerplate following the existing pattern: x.y.z (Development) ~~~~~~~~~~~~~~~~~~~ The goals of this release are TODO: release summary of features Features: * Feature description, thanks to @contributor (#PR). Bug Fixes: * Bug fix description, thanks to @contributor (#PR). Other changes: * Description of the change, thanks to @contributor (#PR). .. _`@contributor`: https://github.com/contributor Process: Accepting Template Pull Requests ----------------------------------------- #. Run the template to generate the project. #. Attempt to start/use the rendered project. #. Merge the template in. #. Update the history file. .. note:: Adding a template doesn't give authors credit. Process: Generating CONTRIBUTING.rst ------------------------------------- From the `cookiecutter` project root:: $ make contributing This will generate the following message:: rm CONTRIBUTING.rst touch CONTRIBUTING.rst cat docs/contributing.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/types_of_contributions.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_setup.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_guidelines.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/contributor_tips.rst >> CONTRIBUTING.rst echo "\r\r" >> CONTRIBUTING.rst cat docs/core_committer_guide.rst >> CONTRIBUTING.rst echo "\r\rAutogenerated from the docs via \`make contributing\`" >> CONTRIBUTING.rst echo "WARNING: Don't forget to replace any :ref: statements with literal names" WARNING: Don't forget to replace any :ref: statements with literal names Process: Your own code changes ------------------------------- All code changes, regardless of who does them, need to be reviewed and merged by someone else. This rule applies to all the core committers. Exceptions: * Minor corrections and fixes to pull requests submitted by others. * While making a formal release, the release manager can make necessary, appropriate changes. * Small documentation changes that reinforce existing subject matter. Most commonly being, but not limited to spelling and grammar corrections. Responsibilities ----------------- #. Ensure cross-platform compatibility for every change that's accepted. Windows, Mac, Debian & Ubuntu Linux. #. Ensure that code that goes into core meets all requirements in this checklist: https://gist.github.com/audreyr/4feef90445b9680475f2 #. Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback. #. Don't add any classes to the codebase unless absolutely needed. Err on the side of using functions. #. Keep feature versions as small as possible, preferably one new feature per version. #. Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the Python Community Code of Conduct (https://www.python.org/psf/codeofconduct/). Becoming a Core Committer -------------------------- Contributors may be given core commit privileges. Preference will be given to those with: A. Past contributions to Cookiecutter and other open-source projects. Contributions to Cookiecutter include both code (both accepted and pending) and friendly participation in the issue tracker. Quantity and quality are considered. B. A coding style that the other core committers find simple, minimal, and clean. C. Access to resources for cross-platform development and testing. D. Time to devote to the project regularly. cookiecutter-1.3.0/docs/history.rst000066400000000000000000000000341262047200400173700ustar00rootroot00000000000000.. include:: ../HISTORY.rst cookiecutter-1.3.0/docs/index.rst000066400000000000000000000014021262047200400167760ustar00rootroot00000000000000.. cookiecutter documentation master file, created by sphinx-quickstart on Thu Jul 11 11:31:49 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Cookiecutter: Better Project Templates ====================================== Cookiecutter creates projects from project templates, e.g. Python package projects. Basics ------ .. toctree:: :maxdepth: 2 readme overview installation usage tutorials advanced_usage troubleshooting .. _apiref: API Reference ------------- .. toctree:: :maxdepth: 2 cookiecutter Project Info ------------ .. toctree:: :maxdepth: 2 contributing authors history Index ----- * :ref:`genindex` * :ref:`modindex` cookiecutter-1.3.0/docs/installation.rst000066400000000000000000000022251262047200400203740ustar00rootroot00000000000000============ Installation ============ At the command line: .. code-block:: bash $ [sudo] pip install cookiecutter Or, if you do not have pip: .. code-block:: bash $ [sudo] easy_install cookiecutter Though, pip is recommended. Or, if you are using conda: .. code-block:: bash $ conda install -c https://conda.binstar.org/pydanny cookiecutter Alternate installations ----------------------- Homebrew (Mac OS X only): .. code-block:: bash $ brew install cookiecutter Pipsi (Linux/OSX only): .. code-block:: bash $ pipsi install cookiecutter Upgrading from 0.6.4 to 0.7.0 or greater ----------------------------------------- First, read :doc:`history` in detail. There are a lot of major changes. The big ones are: * Cookiecutter no longer deletes the cloned repo after generating a project. * Cloned repos are saved into `~/.cookiecutters/`. * You can optionally create a `~/.cookiecutterrc` config file. Upgrade Cookiecutter either with easy_install: .. code-block:: bash $ [sudo] easy_install --upgrade cookiecutter Or with pip: .. code-block:: bash $ [sudo] pip install -U cookiecutter Then you should be good to go. cookiecutter-1.3.0/docs/make.bat000066400000000000000000000157701262047200400165570ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\cookiecutter.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\cookiecutter.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end cookiecutter-1.3.0/docs/overview.rst000066400000000000000000000023411262047200400175400ustar00rootroot00000000000000======== Overview ======== Input ----- This is the directory structure for a simple cookiecutter:: cookiecutter-something/ ├── {{ cookiecutter.repo_name }}/ <--------- Project template │ └── ... ├── blah.txt <--------- Non-templated files/dirs │ go outside │ └── cookiecutter.json <--------- Prompts & default values You must have: * A `{{ cookiecutter.repo_name }}/` directory. * A `cookiecutter.json` file. Beyond that, you can have whatever files/directories you want. .. note:: As of Cookiecutter 0.7.0, the top-level directory of your cookiecutter must be called `{{ cookiecutter.repo_name }}`. However, in the future, this will change. See https://github.com/audreyr/cookiecutter-pypackage for a real-world example of this. Output ------ This is what will be generated locally, in your current directory:: mysomething/ <---------- Value corresponding to what you enter at the │ repo_name prompt │ └── ... <-------- Files corresponding to those in your cookiecutter's `{{ cookiecutter.repo_name }}/` dir cookiecutter-1.3.0/docs/readme.rst000066400000000000000000000000501262047200400171220ustar00rootroot00000000000000.. _readme: .. include:: ../README.rst cookiecutter-1.3.0/docs/requirements.txt000066400000000000000000000000501262047200400204170ustar00rootroot00000000000000watchdog==0.8.3 sphinx-rtd-theme==0.1.9 cookiecutter-1.3.0/docs/troubleshooting.rst000066400000000000000000000022761262047200400211300ustar00rootroot00000000000000=============== Troubleshooting =============== I created a cookiecutter, but it doesn't work, and I can't figure out why ------------------------------------------------------------------------- * Try upgrading to Cookiecutter 0.8.0, which prints better error messages and has fixes for several common bugs. I'm having trouble generating Jinja templates from Jinja templates ------------------------------------------------------------------ Make sure you escape things properly, like this:: {{ "{{" }} Or this:: {% raw %}

Go Home

{% endraw %} Or this:: {{ {{ url_for('home') }} }} See http://jinja.pocoo.org/docs/templates/#escaping for more info. You can also use the `copy_without_render`_ key in your `cookiecutter.json` file to escape entire files and directories. .. _`copy without render`: http://cookiecutter.readthedocs.org/en/latest/advanced_usage.html#copy-without-render Other common issues ------------------- TODO: add a bunch of common new user issues here. This document is incomplete. If you have knowledge that could help other users, adding a section or filing an issue with details would be greatly appreciated. cookiecutter-1.3.0/docs/tutorial1.rst000066400000000000000000000111741262047200400176220ustar00rootroot00000000000000============================= Getting to Know Cookiecutter ============================= .. note:: Before you begin, please install Cookiecutter 0.7.0 or higher. Instructions are in :doc:`installation`. Cookiecutter is a tool for creating projects from *cookiecutters* (project templates). What exactly does this mean? Read on! Case Study: cookiecutter-pypackage ----------------------------------- *cookiecutter-pypackage* is a cookiecutter template that creates the starter boilerplate for a Python package. .. note:: There are several variations of it, but for this tutorial we'll use the original version at https://github.com/audreyr/cookiecutter-pypackage/. Step 1: Generate a Python Package Project ------------------------------------------ Open your shell and cd into the directory where you'd like to create a starter Python package project. At the command line, run the cookiecutter command, passing in the link to cookiecutter-pypackage's HTTPS clone URL like this: .. code-block:: bash $ cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git Local Cloning of Project Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First, cookiecutter-pypackage gets cloned to `~/.cookiecutters/` (or equivalent on Windows). Cookiecutter does this for you, so sit back and wait. Local Generation of Project ~~~~~~~~~~~~~~~~~~~~~~~~~~~ When cloning is complete, you will be prompted to enter a bunch of values, such as `full_name`, `email`, and `project_name`. Either enter your info, or simply press return/enter to accept the default values. This info will be used to fill in the blanks for your project. For example, your name and the year will be placed into the LICENSE file. Step 2: Explore What Got Generated ---------------------------------- In your current directory, you should see that a project got generated: .. code-block:: bash $ ls boilerplate Looking inside the `boilerplate/` (or directory corresponding to your `repo_name`) directory, you should see something like this: .. code-block:: bash $ ls boilerplate/ AUTHORS.rst MANIFEST.in docs tox.ini CONTRIBUTING.rst Makefile requirements.txt HISTORY.rst README.rst setup.py LICENSE boilerplate tests That's your new project! If you open the AUTHORS.rst file, you should see something like this: .. code-block:: rst ======= Credits ======= Development Lead ---------------- * Audrey Roy Contributors ------------ None yet. Why not be the first? Notice how it was auto-populated with your (or my) name and email. Also take note of the fact that you are looking at a ReStructuredText file. Cookiecutter can generate a project with text files of any type. Great, you just generated a skeleton Python package. How did that work? Step 3: Observe How It Was Generated ------------------------------------ Let's take a look at cookiecutter-pypackage together. Open https://github.com/audreyr/cookiecutter-pypackage in a new browser window. {{ cookiecutter.repo_name }} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Find the directory called `{{ cookiecutter.repo_name }}`. Click on it. Observe the files inside of it. You should see that this directory and its contents corresponds to the project that you just generated. AUTHORS.rst ~~~~~~~~~~~ Look at the raw version of `{{ cookiecutter.repo_name }}/AUTHORS.rst`, at https://raw.github.com/audreyr/cookiecutter-pypackage/master/%7B%7Bcookiecutter.repo_name%7D%7D/AUTHORS.rst. Observe how it corresponds to the `AUTHORS.rst` file that you generated. cookiecutter.json ~~~~~~~~~~~~~~~~~ Now navigate back up to `cookiecutter-pypackage/` and look at the `cookiecutter.json` file. You should see JSON that corresponds to the prompts and default values shown earlier during project generation: .. code-block:: json { "full_name": "Audrey Roy", "email": "audreyr@gmail.com", "github_username": "audreyr", "project_name": "Python Boilerplate", "repo_name": "boilerplate", "project_short_description": "Python Boilerplate contains all the boilerplate you need to create a Python package.", "release_date": "2013-08-11", "year": "2013", "version": "0.1.0" } Questions? ---------- If anything needs better explanation, please take a moment to file an issue at https://github.com/audreyr/cookiecutter/issues with what could be improved about this tutorial. Summary ------- You have learned how to use Cookiecutter to generate your first project from a cookiecutter project template. In Tutorial 2, you'll see how to create cookiecutters of your own, from scratch. cookiecutter-1.3.0/docs/tutorial2.rst000066400000000000000000000015151262047200400176210ustar00rootroot00000000000000================================== Create a Cookiecutter From Scratch ================================== Step 1: Name Your Cookiecutter ------------------------------ In this tutorial, we are creating *cookiecutter-website-simple*, a cookiecutter for generating simple, bare-bones websites. Create the directory for your cookiecutter and cd into it: .. code-block:: bash $ mkdir cookiecutter-website-simple $ cd cookiecutter-website-simple/ Step 2: Create `repo_name` Directory ------------------------------------- Create a directory called `{{ cookiecutter.repo_name }}`. This value will be replaced with the repo name of projects that you generate from this cookiecutter. Step 3: Create Files -------------------- Inside of `{{ cookiecutter.repo_name }}`, create `index.html`, `site.css`, and `site.js`. To be continued... cookiecutter-1.3.0/docs/tutorials.rst000066400000000000000000000016601262047200400177230ustar00rootroot00000000000000========= Tutorials ========= Learn How to Use Cookiecutter ----------------------------- * :doc:`tutorial1` by `@audreyr`_ Create Your Very Own Cookiecutter --------------------------------- * :doc:`tutorial2` by `@audreyr`_ * `Project Templates Made Easy`_ by `@pydanny`_ * Cookiedozer Tutorials by `@hackebrot`_ * Part 1: `Create Your Own Cookiecutter`_ * Part 2: `Extending Cookiedozer`_ * Part 3: `Wrapping up Cookiedozer`_ .. _`Project Templates Made Easy`: http://www.pydanny.com/cookie-project-templates-made-easy.html .. _`Create Your Own Cookiecutter`: http://www.hackebrot.de/python/create-your-own-cookiecutter/ .. _`Extending Cookiedozer`: http://www.hackebrot.de/python/extending-cookiedozer/ .. _`Wrapping up Cookiedozer`: http://www.hackebrot.de/python/wrapping-up-cookiedozer/ .. _`@audreyr`: https://github.com/audreyr .. _`@pydanny`: https://github.com/pydanny .. _`@hackebrot`: https://github.com/hackebrot cookiecutter-1.3.0/docs/types_of_contributions.rst000066400000000000000000000042561262047200400225130ustar00rootroot00000000000000Types of Contributions ---------------------- You can contribute in many ways: Create Cookiecutter Templates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some other Cookiecutter templates to list in the :ref:`README ` would be great. If you create a Cookiecutter template, submit a pull request adding it to README.rst. Report Bugs ~~~~~~~~~~~ Report bugs at https://github.com/audreyr/cookiecutter/issues. If you are reporting a bug, please include: * Your operating system name and version. * Any details about your local setup that might be helpful in troubleshooting. * If you can, provide detailed steps to reproduce the bug. * If you don't have steps to reproduce the bug, just note your observations in as much detail as you can. Questions to start a discussion about the issue are welcome. Fix Bugs ~~~~~~~~ Look through the GitHub issues for bugs. Anything tagged with "bug" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ Look through the GitHub issues for features. Anything tagged with "enhancement" is open to whoever wants to implement it. Please do not combine multiple feature enhancements into a single pull request. Note: this project is a bit conservative, so new features might not get into core. If possible, it's best to try and implement feature ideas as separate projects outside of the core codebase. Write Documentation ~~~~~~~~~~~~~~~~~~~ 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 ~~~~~~~~~~~~~~~ The best way to send feedback is to file an issue at https://github.com/audreyr/cookiecutter/issues. If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. * Remember that this is a volunteer-driven project, and that contributions are welcome :) cookiecutter-1.3.0/docs/usage.rst000066400000000000000000000051411262047200400167770ustar00rootroot00000000000000===== Usage ===== Grab a Cookiecutter template ---------------------------- First, clone a Cookiecutter project template:: $ git clone git@github.com:audreyr/cookiecutter-pypackage.git Make your changes ----------------- Modify the variables defined in `cookiecutter.json`. Open up the skeleton project. If you need to change it around a bit, do so. You probably also want to create a repo, name it differently, and push it as your own new Cookiecutter project template, for handy future use. Generate your project --------------------- Then generate your project from the project template:: $ cookiecutter cookiecutter-pypackage/ The only argument is the input directory. (The output directory is generated by rendering that, and it can't be the same as the input directory.) .. note:: see :ref:`command_line_options` for extra command line arguments Try it out! Works directly with git and hg (mercurial) repos too ------------------------------------------------------ To create a project from the cookiecutter-pypackage.git repo template:: $ cookiecutter gh:audreyr/cookiecutter-pypackage Cookiecutter knows abbreviations for Github (``gh``) and Bitbucket (``bb``) projects, but you can also give it the full URL to any repository:: $ cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git You will be prompted to enter a bunch of project config values. (These are defined in the project's `cookiecutter.json`. Then, Cookiecutter will generate a project from the template, using the values that you entered. It will be placed in your current directory. And if you want to specify a branch you can do that with:: $ cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git --checkout develop Works with private repos ------------------------ If you want to work repos that are not hosted in github or bitbucket you can indicate explicitly the type of repo that you want to use prepending `hg+` or `git+` to repo url:: $ cookiecutter hg+https://example.com/repo Keeping your cookiecutters organized ------------------------------------ As of the Cookiecutter 0.7.0 release: * Whenever you generate a project with a cookiecutter, the resulting project is output to your current directory. * Your cloned cookiecutters are stored by default in your `~/.cookiecutters/` directory (or Windows equivalent). The location is configurable: see :doc:`advanced_usage` for details. Pre-0.7.0, this is how it worked: * Whenever you generate a project with a cookiecutter, the resulting project is output to your current directory. * Cloned cookiecutters were not saved locally. cookiecutter-1.3.0/logo/000077500000000000000000000000001262047200400151505ustar00rootroot00000000000000cookiecutter-1.3.0/logo/cookiecutter-logo-large.png000066400000000000000000002656331262047200400224230ustar00rootroot00000000000000PNG  IHDRIsBIT|d pHYsnnrtEXtSoftwarewww.inkscape.org< IDATxy^uag$9`gV;Xv6mU;xjmoj{vݮFP[1n@E) q?Cs $;|>k2߰VzW}_V˓dEp-voQIy|o)ZshW][-;Mrt(&ݎ$'9/I>U75e`skeIN`$Nrp({fz@uo,{h/][1yCP4 ώ$_`D~vMs$3q`VZI??}=(IU4 ׶$do+;wkI~.&YQڜ ? u($3~LVUdiH2U4 IL$w 9 hHtmuX`$$ǔ-tkg0 ?n`juv&$Xٞ {SF0aϙN.<u/`8LVe0nS ?Suo)31յՉyw$듬`@/ffhHVH N?p0{W$yOuu_\:`4#k<+cL->o(2ԵI$KSI-$]IΪ~K872\8Xx?ɺX:`4CkÒɋ릿t|0gum5.ɒ9L듼nkF,I_e}rdm^[%.M<`SSvKqLVNI/26ޔ\qMru͛[o geɱ+{29xܷpMM!shy׵!IޒWJmۓ ůvp|8d`<~e,HuZ:`3zd&OqOwe+ پ}=89^ɪB -G$jr KGU%NH~j5uEgM!w0gum[I"ɒ-W&~-ٺtpZz@S8 ےZ/Zo-2oO>[. G<+J֟'úw&8wks-}?99ƛJU'$?59pisVM!2g$Ktɹ_Km/]2X<䔟Ht ..N]:LF$N$SsFyH}YqO="Yq`0!1g][]IQemݖko(]2~Zɑ.Ŏ$Cb4$g%9lۺ-6._%g<8ϽJ&ya&8I$ǔnu[Xd2*y)IU.IMe`~!Teqc'<,Yt .M̺+յo%Yyks Kz{]쮭S:oNP][UIޘ俔n۷'g~6KL#Od%蓼n7Ɠ$N-⼯']Z$9IحJwTVV+9s5'\X`^0g'HV''|GnmN>׿|$!0][=:ɥ[g23|>JW㒜۵ C`4>zVO&YYe\kKW';v$|9ї.حS|kFI)Ɂ[ɶrk+KWqI>ӵOFjQVoM$Uqsm+vtHZ[:]FckIޛ䥥[ŗ.`oxsr5+Ғ$Jh|tmu@&yNqu`6q`4kwm3`VHV˒|8K2>rۘ\c N_T:FckJK.]0F ߮F$K-nd싍?*]0+Nvmu@`V$dGnm.]$뻶:t0܌GXVG%Tn)vh$jy`xQKrNn$N].gOJѮ. 'ԵI>-J;K0mOa0 d$29neEC/E`ޖ%;$ܐMMoM6ݚG1IRUA&/OVs(li2_S:_FumgI^]vɦC򛧇7mJn-ټu06߼5ٱtJ^6|v>|Y2U+M#c4^PVNg;[%[fo~pg<R Ռkj7v}n%k/׋KWHY7;JGh^m;#/M!3/k_J$U`blN?W:[F k3|<[sCGM!1_@][7(LK2t07JLV$93@Y''PVKKsh|tmu@Nt @$yWVU`/w$y|OKGh|umI~tnkO}醱յճ?IU`%yJ+yҵI>)w$~C`AV'$RK+^7K3U:`tmuhb0{'9kJc4>ZMRnIW#1[oI3#I{`vHV/O}IΨ!T}ߗny][=5ɇc>M!1O]['ɗ^`6%yA*ťFYV&` sYM!*0ޒ#f$0`4$/,0KLSu_W:UFNV|1ɲ-{i{߫ͥCe4>K][$-~yut0Aq`t\iu_T:SFIV/O;IN7V}_a$tm$$/~[`~J$,p7%ݺ^:XN;][M%y_ 1IS7٥Cc4~^䉥#EI~nV}醡յՓ*p>uT:XxSUV'$yO $O5ɵt0jQ'YQ`$ͺO,IY:`K̺-W}_atm$_Lt n|=ꦿt0J &yw >c w$uxC_~S`T}ߗn ][=>1mI~nxI$'N$O!r_`.'y8pw&~4޵Ӓ0׺ IDATZޗ䌺. tmuTo&9t @>M!X\:806%yA*wm6ɚI.KQ:=U\V'FJ$Ϭ!hƻ3@yL鷔`]zymkILxe w eNeIt0v$yUo.%]$L_+kɌw$[vs|u7@`k׬̑I&3{ӵ$A֭߰} Ci&Yk;]e%7(;NܲcH>sP>k|/ɥ3o45FDƻ_f0nJܺ?Z:5'99)I(q-_>U3+UoqZ9v#x"ɢ9w$٘;]k֭p fGf}3;_\1}]9__nkV/KR'yu}Eɮ=>O_WT5i}61vcs>s$~TɮytSv3&Orٺ&4ka2'te9$l,׀{$ORS_n LkVO%y5s ~Xɮ6%|#$M1$noK}:;Co1e0Be'2ϭ[e`t]zIG&ybL$Gl"7'97זs\n-eQv||$+F Cr5W&yhMOŖ$JFgG%$O9ےKyol{cRFE;-M!''ЭΑI(5:vdp#4Nӯ/`$ ^_*5:nˮ|a M5LrzvQ$$[as+33GWdsH~}$warVeNB* & QdpATqAE@eS@qcAQDjPѨ:~0"[EAA%Q;]U[oݿ+;1Iw<$IdߎοF G+y5]I$I$IӶLA}hJϙ=m]#-$iϋ7>b-flQ\ 4 y\|JN6jE+:T\Dg=I$`K:[ѹyfhT} Cgf=/=[x|et$ ہӹy5S$IzSm x5jlQEmVA:AuGz_?%&Cro2z؃T%%h{DGho!!q]oy?R&3Zh4[P5Y }̚Bk㻢tpXt'}ٚb.j4[KG^w asDOվtg^YxOyuUՌ%IR<:־;:D(9msS=w~BgevAg`o:gKNCgf2IS2ϠQ&CΦ3D~vٺ3L0πCOZg9A<~vSrӽ,`|PwF^﷒t}wa_>PЙIH=6=:@^ w?n-9|l]Uy3-%SrcK:l_T5S3kz K#tܼ\Tk3EFer?ߋ6_k"j;4>gZǣ;$IP9X4JgP-sԱ2 po{WfI3~VtDF Uؒt|l]Z&iBJN3W*~ ,-,:Cꏵ/;ćxhbby(9 fGKO C`)*<2>ԾE|[Ooz?wIgV;|R-ۍfc$U6佸o뗥ޙzoI$ t[w/_y8H~pKEy?4aF@~N_ I44?:>%:Dë:7hіt:7%3yubk4AQrj,IAhbf-%o4#4pa5޴%G;ȓ?t;A7[@S= WSE}[z?}IRyAo O*9]Bg?LIBxOt{UrZ 'I$;T>tpp&p~j?OZb3'^^d=E p|_KzZZϙ=F$i(8:־8:Dé"CtWfh6 |tW7iȕDw/ ԤKNsFGl4JNOsp[hj8t. @OKN9LnnXpnגs,:Rrzئl]"I:Ƌ-9}|ٺ$Ivݡ0[%I4ȦvS`_ƒ,-LEt7O$vFkvCsf,Gg˸$IR]qt Ra̓s+{{~ |f*!0Utw-R_>=~H f$ -X{;)2rqg[7YaKNӁ;@#684:N/Isxmvv.9] |afIZYz`I$ Zr:ëR<}|JNmKvClz$I&X 5s;[?,9h~?oy:G_rpFplT?%mR[+>Pr88l%i>C74ſx/ח$Irٜϔ h$ixM$I,|ڥFR97ܒM ϙ=2|$IR6:Nt4x a͂skK _5~|fp)GKN?}F9VxaͽKN_k4[ nݰm-]$I}WrZLJW Qe)9DMpO%e ;$I[}/tITI+;?_'fвӀ{-#$IRm 0:Utm| ؛ÁsT-^]r>wٺ5:Hlи$Iy^0_ovOfkNp4׼$Iq;C$-t`DZ% 7/BM9GV>!IjWW~ Z7)9+9Xr: =p4k4KN.9.:F:%Ӂ9tt`\4m7^rs~jD^ lݡ jtpgt$I%^eZ%6C&$IRӿEGH,tqAic$N=:־?:DUrz1pup@{ٺ-:HRrZ bp3KNh~$E*9=}Y9\K?|FuMp4,i !n4[h4[>K )9|X%8Ggx3)~f&ioEkI$ΏD^SrEoT3{d}`IT;mѱ.WJN/q`\K!R%Xesg%F%%=q`\1xKNϋP ^3 *l]$^JN+YHLs_Mt4d4y94.IaMiISr’釁>/JIit}DtI-` Uf8%-ˁsT_[畜/9,:FꇒkՂsTO#N%Nꩡ-N`vt<$IPrZtMaquZt}Lt4$?W~*IaI]JKN?-9 :zh|-L'I-k7CT?%JNɀ>ypqiW%ݣ5pQ%͢c^(9=s?Dh(L/9^r RUi/7#I4JN;^>\Yru;c6Ι=28*C$\`ѱGEǨ>JNJNědcEg%'jtp*T59$墒sc? t^s\>8GӾ%AҀ& sޖqpӸ$I.u8PX~Xr&84.IamtY8tp]& 9ώ*yQp:c˛nvf.g̙_.ln:i <:>+:D2d` YJNj4[M]i[`i1^ \Qr,Fuot(%݀᰸It w+9}l$ n/9 =CFU*6I ?0Kx1!R y7It$IhF:G/+9h474>g Ǣ;$ n#"82Vxх߇{m̥`t=x醰ɳa-UōckCT[>BY9t6̝TrMٺ<:FK2p"ipAJN_vn&a84ػl):H0qBsXM94.I" `/Sӱf"ՌCJt$Ih9:ΏS4iFuet̒V(w v9քopupGV v-UyfJN㪖YKN+EhJN/.ǁq g͒OJNDH)9mp`\k i bUrZxwt&[$IUrU]FGH5и I$U%C@3gӀ};$;9~~2nu?u;vt}Gt4t+`i!N-9DhJN. N5%C4JN˖ X#GUN,9-# nX::BU-qI%JNV&`$IQtYYi%1PC'e#$_x!cKGb:w,źjoN$I%k)<ӁDGH5qC$I%:,iffz%i u?Iqp5"] l>:>/:D1I9-$MЂv.Rtk疜> ꥒ xcopSiy:C㪾qIDiӏw4|]D._[:$IR֏w#JN9fWEwHRx/wEL9B0\vmt@g/kz(9-Sr:fDHK%#ӌgOh,.(9)9=^ \]rz}tTAu;f`-#&qI\ik*`iˡq%InjFH sh鄒Sg+8gplt$e]2y qwa韎^?:֮ :oNj?:B%'n/,9Gi`)jOJN_.9-#UHmKNKEwhBa8$)FizӢ{.xJtT3rt^s$I.mi%JRq` 4:Bz_G=G,m|  wk{J2`KVrrBӿWOa\::FZ:ƽY=_"U /6QA0([i4[DwH*9=8z}BtT3kI$B4.+[ IDATiE[^j>]W^~Gt6cSCT%U[.|0:b]HӳC4JN{?n*de!RT*9-|$CFI{t$I꟒MU?GH53#:@֍$I*ġqI.90IS~ sk ֿ{ҭpFGJNK MMn*mt*94 iaFoVՄI d%"LjkGGh|1:b$I1ۅ-R9:@7$IR Ec>\r:0:bA>4qIuiG]( 3z(9- 9EQ `$EUsJN~,b"ULԒӧJN~.a5C%iA4{#&M$ 끟+EH=и]kI$B.9͌T.9':b~3{d; 'eZ7.Q8:־7:DPrZ8xEtc+V&%;2 8t@t HP%C}_llź2KT{%]&LtCKи$I*fIN1nTKGW%-8 0wntjୣcCGCi%:^"ɳAie(-Ӊ% *9\l" s=DHCO DC.$JN3[z#:Ba3$I- RYQ3{d%m<"5R i\K$IOиY!9f쑗[GwHR5χ^]݆Oٖuut6<H-Rj|>!QrZFV.9"n/9EtF)ph\?{{[>*:@!и$IDGH5#*74 Ivp7+]{8 6pX{ѱ13 [ DQi%࿀WFH5pq!ꯒӲtx~tTCK_/9}$:D걁Є0EwFH(9vnC$Ii\Ңgd@ymt$uہ_Kt̽Nc#CT/%u "Sٺ%:nJN+@Vet4h/njӾRU}*9mR 4_DGL%IOƥsh\K$IOиLi4U9-j`΍pף+4@nk7CT/%5 uSH, `iH,Vi:-Ґ8Gu!ZGFGt$iJN<\!ՐCZ&:@$I%-2wJNKG\2Csfl !C?.s.xXۓU%eψn]P'%`i< Y)E7~ct4dPr=:Bꁁt^"U5s$Kt4 Sr :D=EDGHCh8[te74Ng{7gWGGtɟ.qXLP*9m|"::@!K$Iҿ+:DTrzpRtTߊӦpCu TFuWyi(9EH]00ݡ:b|;w u FihGH\!ՐC%I$Ige.>4ݤ#Iq0]pFFJN~̌n*{5JNk ,"_< 8jt&"`IO0v)GHST&#ZF nWi\09/ .j4[s#J?:@$I7KZǏs7̙=I+~ ]1~x^tl;:־#:DTrZ 8phLzע@OFHZQ쒓dS[$- %mC)CKNbh8иꠎ6%_uPtTGH5q%I94.iIlިN`I+.p0zktXSi=`Bi4[1JNK?3*^|=:BrIpzY!q`_oJ=иqI7~W; :@)K$Ish\Ғ:ː†$C7w.+gw.~Btj#q}&:B҄_t5pDt Y Qa5*?4^rZ 3Cu`jGGmR8 i|X-:B;#rh\GH$IsOt `^_$r /I=1w.\u}t`ѵcCTo%ki~AVr(&혒+#p%5A>IഒHt4IM'EGhi4[?!74.JN!C x0$I4?7KKNk7|0ڒ3VIꓳ-F7DJN'EwHFuGtĠ*9%IKd:ݒ!W%wDH7&"#z̡q GTg%_*84.I$ϡqISgzk#-IvuGku`b>1JNKfEHZbgѿ8xit%vXii*=4ENٺ<:נsARmFo+FHsAtTc3T DH$IPrͤzgi^=yԦ=-I=uo -=:tht`Nm4[1!i6s*4!iJFSKN~AQJN;HFGm:xUtT1wWGGH5q I$UȍjapH/Yحו~ޗH'mn_ft}JtCi=R=1JN>!kZrrHJN'GwH3JN+GHPM֌"hnFui TK%c; l#sh\7GH$I2':@Rm3zp]I.Lfc틣C4JNӀaIOpS{/VNASR}[r&:B!k |T\ *:4^rݡE8<: MCjg}S[ :@9K$Irh\RG  7>]0'@]tѱC4T*Qv2FtNgqaUr6CR׽ 8::BZJ;FGhn4[#5:BR'#v$)ܹ 7m̨m4٭VcDh(<7:Bٍf4Lt8)9m;CR=81:BZ*:zptZn { :@9StTQsoFGHCq=I$~h\3)۞Srzu7O5oƑyE;O՟ *~~DR7]ht REã#feaPr|0i8Pr9:BZJ YrxAtFUb{i 4.N*FuKt4*v I$UIO7/7̙=2 xo?%I3|nVY 6\:UФNgø7*qTfoBh~1hJN[GwH\rzSt(atbii>U;-k4[?ret4nT %큭; Ft4$c7:@$Ik$kJN֓kfg$ޓfDWḼ? 6 knFڻp*9xEtTQ$f_ ҰRuUr?6IhU:_IURˀEwhԢ48*iGEwHw! K$I etv֓kh|>]GBMk?5bwnwL[nkkRvk5>>h/:bl!)SEGQi*03ERN!Uft8RtJJi舺(9!)SGGH*14E:l]QkP94.i pFuYt4D׼.$I4:@PرOҏ҇kHRe c.5| x=`m֏HZRk"n 1hJNTkGFG!*m%#$*04^rZ{GGTCDw5?EGHҒ*9!U7!иqI$ $ ϙ=$UM]6f?0z&|כa`YFy5es5:~(:F*9=@tTa6@5I%#]i;$UWKN+EGhUs-fUhnn&-!R= ! OfzI$)oM†%LIz}s[% u֬OK ;o 3g|ꊻGEH8!UTl]1hJNoT93ODG*QzKND6hQ1'EHtit$-:^R}l5:B23T9_$I|/:@P5Ι=2 xK_jwu;ȿqnK>+FţcFH)9mE>j!ASrZ$-%FG 3DwHJNHCxu}l#:bN&4Ң<! !7k~k$I._4T;4l痤zˢ zcn~x(H÷-75zwSr*싍ft I5 Tt;or`KP /9 3ZDGTM ih4[WFGHҒ(9!UMrh\Oh̎$IIRh;|Ʒo!i༬mtDՕ =C@پYtNĦ=ś 1(V`t43GGHl<':B7s#!q-Tٺ _+$IR< l]"im?KSқ^ ##1v>; ޳9xaǎ,VYcC؇|* FG $ #3'i׏U%epsdh$f"`c:R;-n4*a<F"5Ӏn$I.fI*CsfL甤AѺanC|x.O~p,lx˫CncFڏFHRrZxwtTAs5CM)4^Pr_1 QrzKtVѲs IDAT*>}&AAhk4[Z=:sg5C}AJNC|7Xo4[иl] -$IzHT/Yc^<)Im߷J+DWĺ~8pi*o^1:Ft4AEGHFu@t ;Iinū/6[#Y:ؔm-R  :b LtqC_KNӁq--ޝKRwþ (7T@ ܗRq'QF}#1zqKTLhcQԸQ6am؇}`3afΜ߻߯k3{oFeU_ |l2t7Tb9\\{,.[$pr>eU_D{UsE= 8#&I$IXF ~ppݒ$?Y l;瓤X>FUiχ<&:D0N':ԧwD􉲪<xctIF,_D +lϲDQk+c+1Iz3z<[<߯{+vgWտʩ&]giCRV!$`i\RVEt7 \:@F@Tt7sC9in=n=n|_X R[ܻc{縝n9|ͣ$!|0K/sIR^甆aYzH\{,:p'dT{Ua=aYw_Rx'4jK`_t8zjA!xut5r R2j.c;y <wd5!CCHj72"_tI"VVAZ4~̈_!I&[zqmHX,WJ2U&t?ڧk L^l7."i!41X)$ NlpHBi.v IS,_Y-8[q0LD5xgOk^YV!$I; 7:}:o p|>}f8s[g[Hg-}y^yGR,K[`-0;ֽ%I-tqPoXE}u52E)4G}ߏ8xUώNzW/DWDP:tK'gU}Ө^ޔ[gKƙ]Fq>ܲkNg[Qz507EjoҸFgƏskpF$un9 {eU/NjU}7pZ"϶ˀۍ!F޲IK0\W,_FeU_ȳ0Ќ8%i^(]s/N3?m y+<$wOMw" k(2${˪&2LY_V&2F;"ȲokL o4b܊< xQtI"WVoFa]gsk!$Id+l 9%8TYw#~"`!]ϕU}[xXfXF+/Ijk/GxY[7ʢw2${>߇N':Q >N8$Nz_bj<:4,E=xlt]4/?LuvYի_*q%:Zgߋ_h1!$Iz=/:F,eGަ=$M.K(%Q[F[6o-=M(/:~t'Il$`щfb3xcox1lQxt (yd`3)GAF{ZN(lzs˪>*gyjO[eU_2.jރ";Yrndyfg[U,:Ș4R-}[eUE~"Ս &׳4jiȳsjhh$)m%6;`Yߋ"I#a,5r5j⿅Q$%eU_ʪW;Gvg-ȟ/~tYOa=bLOMwn"ЫhdΤ[?1:HW-諝n|ìVVUOU p`h06OKh$~ |^Y+U}&pfgLwxNLgXa.a,EgdJ$)8nm1iv+L8_1ǵ$ɩfSV{>N ۦ?<񵒤9d<v'/8m|;65‡Zȳt+].^V A4.>Ew0Y] /3 #bSi Ƌ<{8e7ϖU0һ<tϭӸ\eUDR&<{oB$ ߻,Hy0=87ʓ6I\X^&Fؘ/-tG5,{$is |]Zp9pŒѼ֎s~h^CSӝG0,{r?U}WtM"vΡNyxt7JYLrxt1ɣhh~ yY՗GGYw+쟀x7TӀP,Q8rϣaY'G$Iymt e˪mtI &E>n!_1ǵ$I#*^q`xUg>'AEmUVAFx]VEg<;x=A4BGbi\÷yN(l'MÉ!ZIwSITXo[H9i\ڸpFc,k/IjFʪ>ȳ#hO9IlYO>-b*Xvo]{.^85ݹ8:4FƛveG滶6BUAW?ȳxja\ f5I(VVx pit Eۯ4Ӏ6VVtKh6ay30hkCH$yZ4,Js48Fc,k/Ijkz1wmsh&rca|$Ir SӝwLMwVF@xqYo/zyti-}8bRU}:Y4o-y3"eU/2NeU*G?Σ4ax"϶1,뀏G$IZsCh`UVuA$)ئpn^1ǵ$5棬/'FA. 86:Vsh>\V˪^$%eU8 gf{Rtʠ_$I5Sӝc;0RMόΡ9 ڲGfqutٟUMPVbiձIԧۚeU2:Dʪ^X.lUl ey{gtI8*:򥲪1:$%jRC8 7kQKڢ5ojҸ$I8|j RBBu&𲲪ݱS粪IzƗҢ!EmShN>)ʪ Y7'k43oU}FtI6Oު8 xKtIJX>F{k58$-vIiϹ4Y$YSӝ3H92:6"eU/"́f88::DJ0Cs)_ta)l ΡY]AUtow3f٩ȳ}C5*y<1#ʢ,B$i=<9^ZV 0K8 7kQKڢUEP#4t`AY$IHߢ;a R,xm 첪o"mLgΡx7 /Ρ9;0:J5^+WU$:HU Y7հ;iؑ||KCH$52 / 8K8 7kQKڢUs굆bqIaSӝWNMw|$Og shV. = 76k7U8:D vMDЬ>^VI!eKh֘siȳHE PtIR4K7eUBҸ47584IJպxY7et 43 "I$Y |jAlB0GEFRVCAY-+}DF] Ct6F IDAT(tTsҸbSއВ$ q EgCMp&ƥY`584S??Iii]itnj|__t($IwptA=.:6ToCH}rbKVayʪ> NtmT+&ypptmбeU-ʪxpGtmq ˜>+FEltIRl@4,ץn9geU"I $inǵרyqRjV˪n@3IO $I49A4W{ȳGDЬGhҽQjˤ'[Fzjt 7uyI0כ*i ˪':$uRF۲Eq3inǵרyq4.5W+K=vk$g D A4U}etOGEЬnʪ|tj"v1O WEh CZ<2:ZaŰ"h Y4w_!Ij%'hjX,c!$a,Ks48Fc,k/ƗRs4~~t s]?xC"IR[55鎓9(lZ7CH滴}j7DЬ0m)^+!Z!4GGP+妊 FD}벪WF$q[g{&W@YNXbc^1ǵf4.5q ~FK㋎_UaxtA>:qtY՝R?< xZtmНCYY7C:0:|y pXtӰF;7FЬV" ɘhn~WtIRkyåY4IIqinܘ&kQK3u`Xװ,IvLMwN"5h-mChRVm!&NYLWKtϳ;F:N/=*MN0l9ױnz'I!'kY~R$inǵרyqRj֖˪^cB,-~`.e' F69 xt RCYO˽to1A3!&AYW?Ρ : :<=5:S&;+Ch,k6Xgh~UVCHZmj"όΡ ߲BI8 7kQK39VjFbq_08?$Irpt RYO˷˪8:4h~e jqKہ?Ρڵȳ]CfۉXKͱ$IKt5ޣ=Ch>@Ҹ47NÍk@j M++N$nxO R:$i0ƥq8Fc,k/=Rs4eq_IN$M 'NMw~Dj łȳ}qT&ԗqwT`@u|ۢCL%=5_Ƌ<xK@m eUB4,k4M+/EX $8Fc,k/ʹstIDD K$cSӝH-bi<˰ةf{LtW끓sh0GF:@|4:ֱt5&~AAD$M KXgVﲪ!,wm@jq5jcq\{i&'K͵utzQt /4.I}>`j7i@k\VM!ypbK~YV!&Gz5faKiItIWVOshcy5,Z,{t5ZR/Fhۻ6 5p584ƥjeiȳ}CsLZ$ ocj2:BПH4974`@!8m<5vZ|0:$iP4(7:MӕDhN":584ƥ*:7ʪ/_΢ Sӝ;H!yrbKN {QaxdtpRtTV9qIRu[I"<.:g`ZU2:4i4qinǵרyqu=;:m;hY>qˁN$M{LMwk4qrtiu\4$:fآȳCxZF5B/:fLYn$Ij7:MS@U4qinǵרyquY5"vR휲 -/:~$IRZ|#:4a,a)pVti/=D E:vЧGD[Kt@x n[G$M?+!(nt!͑9iI8 7HYՖJ5j~qu=ȳCH[&>:Ą9}/ŭ$%,A _t+?(Q>*:ܴ::ѴҸŹt@t&`? N|,>^V!$IXكC1,䲪!͑9iIXlk<}37Ƌ<{<h܆p$->5ݹ::44H;-p2NO˹!4CcJEm<0:;sy֦ݘ5^Bk\|&:$I=;,mchn"&46K]ƥqC ]qǵ`3!<>yU`,h %IJD+;&.(l3`,<窲/UoLMcJqrJJ,tY{EPc=/:fHYKCHC?yVx6ȳ}sh^UYkZNti1\wY^ڰ_-l 1n.7TO(f*X~/uܾn`ߥZ |j ҄{Y!y4 iutФ#h ˪^BZҸ|W_!Iz<Ԍy0JA^gFyC#=NÍk<^,. EFҬ=iȳ'1~TVmh,:p۝pmBͷ-wR|{l l)l)lٺn%=l ,pQ)uW/>:$@jwTYO4&:f5:@,kʪȳ%Y߁WV$iE8kxU}ct>fiIi>k5ݹ>:$x*N XjL4if )@jK!I,=>Y)t g`|=@8#:'KiN.'鱔u8xq[XQEtKK;ZV$ `[jyv9ڏ+˪sA5RNOϙ>Y9iIirӉx+q8KxDҍ"V- :s}JVg ˪nԵ"Ϟ=:|?WZ_iyBiV3W%xkJw[¾{{޻æ J xtA$cbxZ~@4&@\\V5!4'F{E4+2:$IC% P= :F^ \ *lS`*:ai\MpZƻ4&qn ]qǵg!}tyJhнFE |7Ջt|IJޝw/{˻ a]'lUt:Un^5591:﮲! 瓴@:ȣChVie<6e8i\jU<:$Ic%sֹȳD~ZA:a.5CH}6orx,äi1\wYNYվ_ԏHq )yp"޷|IJIg:r%\u]q9A;G'ESӝHڠ]!CZ,5Z"vRqrtY#>șƥfRtIb~Eϒ'gUx@t!L&4^yHc~@ڈEؘ"6<::˄MYK$NW~!l_+t{ = #:H'SKi4 j^qZF.~ұmg[U}Ot;:pS}v,Kɲ!IR l{Uٯly2p%43Hfwp5g1\wIrҸRהC$3J㋎_9T,ˢKF[rc!^n8mSӝA$mXo面s j[W\tT \b#9 WU}etUtoV,:Ȅۣȳ7 !$IjYD~?Wʪ!,Yx-%=D+hV?i<:6>,._"T1E}x}taDҜ@q\@sr~tфx~m"aN!$IP[~]iE=1:Pù_Z! gڜ4yHpb~9i\ۻȳmCOg-:Ⱎ޹@v o`ل uP.:: 'NMw~DR_,ҸxZ.94&LF4x~m4.5˪vIȳ/ӃZJZ)zyti>q'Gz&0,._D"ϞTfQԵ갞IJB'$ _ -K;N\x. Xjl hḿ7:k8i< \fdtIO <{Np&+:fpe5Ɲ4Ntû|cX*ȕU ϻ/7V$U/etZGg_+a=ƕ KS+SǯsMO/ =IcstqSYnɣ64Nǖ`Қxv iNCHuȳEi7K3TN Nq'Gz,7ưq{|/Wjd=y&j>7'sҸݳ >/:E ~x*\Țˁ7LMw15qge4wj=h+ʪðf!:h…UotNƛXj{E$IlgEmlC3\@ϴ-'Gz =qX?\R4&xFt"nSʪ>wO/x vvKsq"X[Yu:pಫH#w̩ΗH7K,-5,46t$=iȳ9G n,ꛓӰ]tIUU}etIQGy!ci\Me3m8݌Mq'U':Dcm\sIrҸ"vz"6) 2hVC23/n='6溛WDhNN>W <595:4ҐXO4g䘎Kt k3YOq)m+F$Is"ϒv0K.:4 KisҸS#=͊k\<5/' ωx"϶ڨkjOji\a:9)e*pmIpt x<'-R/\N'7DYwˢsH ҸoU($5Ǧ<1:Hu8i\M2:ff1,lj1\wjVj< qeU Wcueq ip$|ȧ;wF4T- aYn /ϯ4X>B$mW ZJz46';iwXhlKiH|_CaoCפ|(:+l`at,:4n@o7F$˖ܔ,󘔞ہoF$IC1 4:Db܈OMfi`2wqru=I@GOAE:|WQ Y≈'nZ(ȡA\lpE#A"G$L2GLW}Uz>AۚJw]oK=l̞a= I f@xho$  5:${,c[8Ɩ73 >I@i<>NPf. #43qz`Q^kcljR)bĤ3]I IIi`xv:*ă?(?&ÍQf,Ȏg9ƌG[AlsZq=bDi;)=OH:.I{Lqtٲ;_ڽeiSoZP*\<ƒʎq` nyQ2I3.a4cku"^ NJ:E7 a?P$Qf7ƙ43 qc(֊6aE*@w,$n"1i9.i emt4q0:$IYq` nywXK,c"IC`(ǃOf[$Ie!"Bi<>Qf8ƌV!~( {ĈxwP-I:s(VlQuJlnIگ`A'ⰱu (ǁ)Ťq{06:Va$}:7Ei<>NPf\S盘43q`( D14},IRGWyHu[$?0:U( sl=q{Lh8gP no g˃"CA9>LEq>#n3qX<9kxl814ƁVf&J調,N[[Wr;Y@|RdXa(0nF(ǃX@jc=ä(LJ)(3 q㦫ccF#N(xlsZq3 bDi;/%:p(k{\zQe])T$`0QF.l{LhR^3`'>bt4-㕤J(;f\_axX'6mZ+7HH,PG,u޲ iS"tx ><ƁI㨂(?H PnuHb&14&&!@GnX:FMW43ĉ c(֊67BlKZb"ؿ"IEf8bJ{Sj .PŁ8e6ql4q-]MWI` 1SҧCp yǬ`-X&)ƙ43 qbmC( ĺ`fqf7#Й% IG8eO[͕6:*RI g@pr(Q\V9¤8pX87[Nq6&i : wXm;ICDx624.麗ƌ59q`( {fu 4j2JN;֛*u ܠ ?0: JP IIG%ivuvPDŽ=TKy80NOJzeYPiFLF/;(&Ɓ;`c,_ˉx<(峇Yc:@8_'&(Gǯ:1n:5Q^kclj C*CjN:H(cn[䯈B춽զ)PbH:`'AT8Lu`"4{FRĹ24&c,_iuBi(6FŤQq9t&Ǎ59qb*n( {.B#SIV 蓴uۂH[@]''pr({ZxqA'cZNu㲡Y#Z)\u((ǭ:!&Ǎ59q`( {Ćx;-ICtOL8r7O:r_i{+%?0u=cuBiU:$1 X' 1_ikXI@y&xwu0$iu$iB%>Qf=2R)]<8Q*El(wƁ}9IZ>-LЂZ' .͞a%R{_?0u=C4*`zK[>X/f:$q|-+J4ۗwSC̷1WgkPV˓)mu +kU4PQԏRĆxw[>-L3J NQ}m|?k@O|<( X&Ϧ )iLƊr4J@*L`-[3U\8Q[/YZ("z+ I/<@<: J櫒\l@P\LaxbIiג ޭ'iuu]qtM!$Jo0mDixqOZ-7ƅl'i0 .ngOgPj,~WXd` ->[.{[*w:=I:D ~^vu`^4uhefX[,7Xlx1i1xJU!*80{2]7Oc/DgNts^;QU&*:0HZa4^&PBbJqZ$bpw[|:@Ie#ml˥q&Nj)X,7XlxQG >!*80T%zzJݳұIS&['AIMt[k4[!NvQJc D<hxPG;84:Vy:\͒tuzMI]a$:UtuC㶮uC-C:@a{HފqX$}:DEChZ`L˞~:E5@zҴ^>nB%zF6: qYGKCpuHQGǬ@Rs4h ^;>xMS߹"IAIwXtkqc8bĤx1\Xlo[b1,tJf# 5`44_L Aok_okspuHv ͰPI=cb [$i}C-{[Ɓp~nSTX=IY(۬`DGX:5>Jbx[,7Xlx1KzFS\ŤqnMfK/9DuTTS%֨ը&[ӸPXƉxjbxwI:$?e\-u-BT-hX\nuƤqĈIbe(bEiVJ:OTq`>I1Q}VX@V{:Em)x Fvu= Pґ!qJY͂?액4~u5rɒu :ݫCP1OHzEfOX)!&ǫ&P@4[GTEɱXlo&IAѷ)w(h I%ivff<)_Ɓ X>qmrI&Y'AUz?ac8cPj>"mxPg|5~GF~@o x7:P1Ib(@4{VYNgx(ċy(ⰭEEY&c%i{0=X!&Oғ!{NP^l+g=I77sa nwLE1i<[#IIT8{sw[Y!tuXҗCPJ+I묃n7cW@xX/&퍢+:^LGH:_q6JI=cP֧O4urr;I%ըfTIgKQ: 8m!&Т(cݮ!0`K-.quq=I3.\g xJ]^II-SfI= ZlL@/&#FLqFxm/J薇%^$.i$͞;4'bb& ãK'?aMsgZtI^{w `@e=^'HuCy:$IK:&iwX,{ńX/!0X 0ݕI-@ <)G.t7^$eSKbhŤqĈx[,7k8lxQG;.I$=,<4o`xJzJZ'(v:0$okߒG.nDIt"I%͵Iȹ!0yjARdbx<^['Ig`ݖs`J@Dҗ$Rq IDAT:Z,O$,I'l#U,J(NjIdbQ4^sa[NjUcpzbFݷ4+ݍm(cL?jI;Wcn$gCi8Ⱑ#%]ak9:Bim'.M?Pj^wYz,^#n$_۬`TG&ivu 㵾u#`TLW %Fxm/&WǠ%ݩI0ʈbQF7JuNQǘ-NP#/muc3$}Aiz]e@%dQG9P&̵Q7nOxQ(h+/J{$~equfOXBiF ֒NtuN8F1=C:H 㨔$59zm}0Jx =:!Jq92x7:!xms`EĤl. bI3C`%iVc,|.xwu@e= _$m@f$iv9qPߞݭCm4YJJqcx(TP ax=.6I$ޗ/)[:D'ĉGbqa+ä Y'n%-hk(Q:N!e8:8:ֲ:|=xu@,I%i9Wb8I=,90[x$mlQZ$b8bx|J4PfҢ4mKIZ1*&&#&DKJQ]ӨT(U,F*N>)!K`4+9$͖Iz:xen+XEJV7G7C*cW&i$X0m<~[@i;wC4M)x7[T+zF8X@a.w(iI'$ivuT q} mm1"˶Kk07C%Ԩ׎ jeً6C:ֲOpq]ax|7Y JK \`sue;I'ZFAinj^bk)[iIqzon*x7O<X]I-ʹBҹVX !tKfP AL,w[F͸Zi| =F8JQ4NjIX@%Q:!Bi#ZĤqtܡX'vF^;Qo@T(ÖfhtHd=IZZVga|61=)qos4{$-6΃P!SIYI΁;:0n^+3i<~LP*Eؿ/J~2J@M1J/otҜYIR*lI k8;x7:ІX>:Dzu T$tu ~&x΁aQG}?Ie|I/m$LdIYʏ}:0`TJqTZhaq{߱ʢ4tnWI1>IZ@:w^tT$@̕tI^Qc9J1[{CcIls`Xi m"91LdI_у^%zI]%MFY=;R}+gb80x*0Qmb`:FE8L"Fؿ/J3,n GwYI(`+VH=n:Qϗ]YJ IZ]@ # mih0vcNu$x?[1XFBCRZ/4K$} [ƒI(uXTY& 1>47VeEEc.w8a$x/`{ $}:Hu +^ +,,hkg6ց2MWCm4ɒ.+w}lú:8QgwXhú9I'CR~7ʿ+ ʁ|+xu@Jn΁m'!5PqĆIqTZg;^m-J4w[N_+1*O[W:z0>/Fvu8QT6/ ~o:LG8i9I}MdAmk4^> XVujO:BiI_΁Q4ޒYֱ];Jҋs`D1 ݵRmS%=coCI F@F|etQnuxP}`LK47~?y:zwqؿEiPim)鳒t/ElI:&cDͰN]6^vR<(̕tI^QqZ4^^Zh;aw)P sSr8Z҇CTI&90k4[nrwfI+9A(1 -91ix:D8|Cؿ/J㶸mwvO=I]ٯ4{GfIzkQo ͳJў;['莾>i.҉GHsfY0$ok_okXUL/u%Gn 0(5CM%G'x7Cݍk;I2_uCcCTIbQd:*Npf]L̟Jt3C4^)3i<~`LJCEc.w8f<1iI/Klp2Is%+iQ0}.F19N11̖Nm|h 18 @ؿ/~78e,RI[[gA[P::vzKt xK$6~Jxj*]Mҷw3bdt TmI߲Ei<~U4Ni(ǏRiq;^mo OƋuuf?J&I-IRf2Y:I/;L(㵚+_̷.3[Xoa`c]sPG$4^N ޽:D|Vґ!ж*Ư::2IPk;}W&YЖY*I-V1mKI_Jˬ`N }:z0O:ڲ::PCؿ/~7b}iaftF ?%o`ELE̒?Bowi$Sבة9Yy(\Iߑtu^PW M+-Iݫ =`+Y(ɒim{\!&*I'%:U ~AIGY@[L.;4Ѕ$~*)RyM5!&[@5$ivs`-xwuĔn"m%memax(i(wqؿ'nZk"? I٩B X=9qt&W@qlI/SmG8L[Eؿ/J8<1i87&iX~Э\ΤJi6}t>k_,f|)uבvZ:p#&N9>IHZШlk9t /︃!,]n6Yw[QnI)i=,tU0!gj$6%Dg˃ϒCW&i`jojAfXpn4^ %Wn7 )wY^mۂwS\{r4?JšT;^mqy4^+C4["wW[I^x"o$iQ ?@imgPsg饇JbyҜY RfH1a6Iinu:mSQkiM_IscY被I'IW6^~%iumX@%us7%]ş/.3,D$iI7[@Wl"YAXhczQnC]vͬCm`LJCEc.xqp=5bn$~#KyY~Kw䨞>iMC^f#SyI[l"@hFp#^}}w6H1@{vtE^Q`ZnuxIWﶲޓc΁ {UpMǂwy7M`|*3i59av=Xkݐx!IY@%uuJIgy'w:qtƫc3I d=auiCtٛ#Lb,OҌb]osתּRU].LtTIo/Q}MG1|}v' /G==vvR>:5;^RQӨט| Du9=,$=:zϭ+^$ݶAP88 gAh]%L%]{u+$mduTVWNz5'ܦJ^n] ±FiZY:*.h{wxк! s#UUEˬ ]xQU!b I-tqU4Uْnmkkk5@$ImL\n]ӯ (RK$B7=* .?w +U`\~h۳s(ε( .UIeX@Wm)wYA-lb*8McY 1JQ$͞Njߍ-a"OWIAҗwSz QԓP#Fu{z. }-x$G"I?K:f_. m`&oIJL ˭Jz:O'%]:Lwt($Z@euܝbIυ^dP ~l]w/bxy|0x7:DfIzutb0VNj>/~7ֳJ[iGܚWI]b\ 鬢lUi` I I7&a^ٽbIz[w;9jIz?֒GҿV-Ijr }0x7:LwӂwT&r[]j"ۯIcN?ũIvnu@aZ@%:r1KҏwXnOrh`b.Mah b0VNj>/J㶶Jcx1r+'iUg6+ۋxR I:E҂Fvf^x~`H%]:LQx5m-7w3tS$I7J:: $!r:jް}tSnWI%: &dPR:*-xf+%!.WiP\4\eoV󼊳hhiCGdI΁q|( ŠP +`q[[Y@q}yN $ݙs`KDO$%\`dK:Q`=ے>&$]X҅RFn9ƫ}5oj^jxcw©R mɹ`] 9%j5*8 IDAT~B!w%"@{iY0a%i"H)IJwϿܼ^;=I rGiyX@nv4?x15-JKv!жK* qKˁBi1( x1qx1Ó4{&x^1 ^RfI+ vtE^w aI5xkIG[gA&I:Uҩ՜$r[o-x7Ii7^)>kwHn.$͞ ])X,#dݺNt(<XOww$ib(MwY\fE7][%.Olmfykǜվu\+,+JItef'/ݧ$}<ώǨs̋r4^Oxޘt +/!Owku |>ϑ4,'X)SVhSQWwy 'zTIj=[,FIZIu{kL5Kݳ%sSŸ{l]tz2Hj ._TOQL$FG%h9:Y~))1}WLn5c|M74q!wJשyUTo4{:*-)ϹHҩ*%}BL *tEk4 Xһ05I/l=?%R:skFZ HҔ&Y@_g}?SE2* E- lZfF{,IifK-t7" wu%iTkzuj=Iݟ< I3iG %R͢,^e?+FCIޝ!jU3h:+x$(@u!JǏDiWMth񜥭b=jN~Xej^ZXw|L2L3HM͛n%]/Jչ~CwZK6xwY~[ϐ)Q^ &2=R);n~l͐$]h:@x{<LjxCJkkY<W$i~^X~o8ܢMD݅IqFJDiW#]f$r&S͢Iʗ[~,JlwIH:'xwKYI/V(25HVYTHi\46xH碞IJw{$iVkPw[&ivuTεj('#t6_m@ IΒ..sdnǚ*Y)>hǤ[Xaw{G$͘ n5|$Η"]5'Kiu(>IHze^ K%I!xIrY AW.4лK: &$ݔٸ6j}%FhҍIb ?S:A`#y|/ixoњ|环$iv}^OBi$ifMT?Xaw(E1ʡ4?0I<77KG%?8!Nkk=ߒ@E%JJݥlѨIڹ8- #>fAq53՜Eo[(JfB Y9k=:EվT3M%B Ɠ4{(xaI_,yW&~: .N ISt湩Kq(JUKz}"I-n݈Nt_ j::B4Ja;KlEn`MluT ^oݜcTscワ٭#PHe^w `K$+&Vp`$ͮD1u՜Puڳk`8FW}tNqGtoeIH}_ ]SzfK!IoՑw%j(~A 'I'Z@.U }[saKlI? x7Ytw!'Ci\(I?=+iuwNLҌ}Bi<=E&yFϏLYI/]mk_'e٢ݿKzu"Β$ͮ .iG,*$Q+$%EIq!Ex'lM^x~nbNlu@m'O%iC0EiKw4[h&g7Z@fI: "wXңjX_֟T>4̰uutRi +c34K!xf KX'KRf˃wOeϏ4xwtf4Jz[Z:Pb:P'(J7%T޷UuCgq?IJs#Iu@nH4xwm_8nH,?9%uOP$Q҇%}Qw$.IIt(*& -`LL]{KnK@(5Jd6&IJё&u WovUb5zm0@%ivY*O;ң3kI?a[" 'i:zƳ~h;: WJۂw'wuI::DZԗXJB4Ja;:W\jjNP=?>{>0ѡ*$͖g8K.'nkokwIھ[O@(i~^X9^oh3}8IA,%i0xwí%ifY83#KzuruÓ4+xww[e@WINn$(t x'isI[IںuwK]jY$XЉۭx$t{[տDrI+:kf,تtXL/UuìCJ;~=VDn=>,IEj|?ssk~MҌrq5qn:_빓4cI'[g-x$Z4rH@'Լ+g&Ï5! h41I$c(+kgT,젂BQ%(` j,4#X#i"M: c–̝w>k_ C&ˏ#4S׽x ep&^m%wfźmz;V^2$(H_YMnDVn௯~\f)rY=D6a~ %-ȊPx}Wp/הr~xetp3:m\])fz#rz*w٤I_?C=888)jq5sj3֗{_ݞVYh$MӀpw8c~Zd)^c@A S.ߎWIK59'\nDm|.:$B~~~{80 oΌ6:~s*2PҼl<8{{?ͪ=$C\7;4/XXsX$IAA!>i-R.'߉Z#\.2>rBtFx_tfR]x# Iό~{~!RNL*3m xf=sp j~~{:H!F^UH!^<i4xyҸjs`'F!u4e^ժg4o._^I'/ K۸:jƥyCt@M|X!.>Q3hNTFZݤ8 x ipf?{EH-n/:&> ~utDE Ec)oDwHjXnlMԦg!U蔳-s4۶N/ OZ*:"ߍT Ђݡ{[׽t${ݍ/nnvWC$-ȟ/:$gˢ# xItĤ\jE9T(^?K5k:'v!37ǢN_ixK4-[G {GH5vxmtDMxڸ8&OZqIw+X{@f~ItxjtG{? w)r-%^5m )ԩZ+fC]=sh\hcF6v03KYl܍/$Iip8;tÅ^)VҤnQ_Ί8{Ot:\NatY6SU{ rxot&Auҩxut~>!'GGTqiyx3]0|v(Wϡs6iJ Q!j*F:/mh_,E$-=Ӈ N':H r pntrYHZhj3)s#4x8\{K}`tpi^u7`or =Cj6n spYt07CsTQ]u}&Mn^ڮV-.м<{!C$=mciK%'2I5~qtD |yt-2)__Tk7uۣ;4q{MCxyt4O <+:B҂=v#"\2p\t[GGLxCEwH q-CsTQ]FYv7eڮVmS.7м܎n@"I&d[siʾOIKsO9ßo_M0{#$Ֆ.j94>v(P#4q":BZ60*nJKӡ )OǛ y*׆B\ǡy}ן׀fn':^ժ߃瘦xQfO$6^ 1tv:=FZRKP)ߌTk7])Ӂ/FwHEGHc Ub~E%:@Z7zfsDGDJ|9Cj~ sh\MGGh Rj94^u|v/!3I)Ewh^6ekJ;4lZ$I&f iR._ZA^_#jMIє Nu7Y~i inxLJ Ћo_EwH:j|BC7qTxgO@@~>:B~{ 4pc$Im 5t:`wT7DA4j2]Mr9xtZ%區Ox[t= DGHIh)s'i\tZ %2:dQ4ƫxWjx7Yw\.>ݡy#F%Ijpw8l#Ue|;xb׽otDM::BRrAҼ0:BRm&2:B?N |6Cأ>$:bUS=,:@Ң>65xI)c7@y}WgueEt6<Մh3.^NMK|N:/:Mtpu0oIr:d)I#\!Jފ^wyrh\M-\%͂{{eƛ3CjDGLR<-Cqh\Ѽ3fhs6J)x14r9rtesM_|,IIJ݃)DmcIK\! ،88?:BRmp'#rpdtPFGHkш,)Dwؐ~yek IDATq5Ki#Όn  ijo+Z̡qEsh:Fh^,F=:` 14>?0yym4pb$Itl 0t>9t#MR(JZ?vj)ã#Z數pCeOfUt\J>4:b2:BZƥf:H!5\>0:b*:BRVz6p:>{T4x}7Cȃ&kZr)0ߡc"Ih#AgᠳIt4ARVK|NtP1p\,:4fhkt_L|*:R.+p4K;rDtjg離;T#/%=,5S舺H|6Cj=:bƋvόT4VǡREw3jvct@X}4o/^?sMF$};';JK^#b<4zdt: U(c࿢;$ME}US.)SmuGpiu!iQEk#!:TߏD\b*Cnky`t_'r$prt1ڨ~'GKN:/: i1R.C}RuWFG4+nr::dF0v1rit P%uҬ6pN 񳠴81aRʥt3TǡREw3F'мtXƙ ?v2-$&|8~8l#-һӢ#{q,:.R.2:ERrYtĬ/p8Km˷# h/Dw2mn~ 5C㷒r>[ ukGGh^^$Im-pppйkt)[GGI%߈To\\h;ea;$Ut<4vh|lOhxb#&(Js}tfBTgIҚ0Aw|mt6/qpQ$ImpppyBt!5?{EGIw8L/*)ˢCfUe<Iw<}ѶxGtm2*5CR3uDGQ$QWEGH/Dh*)xe 4.oJ`˳o7$Ik5pp9|8;:FڐSC]8--Ϋ&EIΔ1.r0"i~hUjF4r ;Cyy׭Ðݢ $iqEGH />!i"~r9#:Bxu~ WFw3\k=[j3T[~ x-BD;Nb$Iᠳp,:FZEK;ڮ+:^`ICI|'CҒ\rifJ[ޯJxctڪ=|/KgurY d/iZ74>v pyt%|t&j\k_JUn Q tmtT#mzIZ /44lI͑$Iu)ppa8tuI|4Cj]#&r,%OjKժ>!ivJ::BZV \VGw2;{?nh6Rsz\~.Cj{֭sH\/CҒ|):B5*.ph*^Fvt5ˢ#A_,q#'#I@:DHF^uQ7)1w'EV˅!Zt_M|!:BZV \՟7ifCRsy\>|=CjGTF>r(:Bp:ws8X*ݬM!BrPt6h#`[B94.Il8q82t#Ze[3kGrIs[Z/`4/%:BZ P%^u |6-pl,5Cr\T/-ĿETah?;$-ʍ>CwEU^pTYC\OE34~"pZ$IRu3ήAg R.2iia^PW)C;$-)#~)!i^NvHsU\~ |!C3Ij;{-#$-fMrxYt Jk)#TJ'ρRՅݐr2:BILa`W cڠE /[>ē$IRm2t#Re3Cj{DG+m&OIPtxZEj4'":BvNE|?KIr&!!:B~!inSΡsh\u=y^bf{-#&ġqǁڠN8r!$:c[DGH 耺JlhhJR}r#:B zKF)Z74r97^iz6 94.5C zR6:*)[$'R.DGR;y.U+!)kHu2k'at4^WFҭ1.Ђ84.C{*;:@kե4p$I|p&:Fa#[rEJ; K5_R.E(F?gDH-r4T5#fqR.?Dvʯj;{M#$-'h|/[:: Bs[r#O8<<č'SU7|59LyT-Ik8:`\3KJtnsK8q82tK< ^tT#\Όh'4iRO\X)2ytO{f pи2|_K K<!E;DDI\< Oa5”Cc|ΓKͅ-Jڵ,9.)S~=i|_K6o{,pitdV6\C妔K[yeՆCP{rfF44fZ0oq_Ldh|r_K$i=AgᠳYt): O#&W[KQ\V\^ #R.r=Cr`kz",W\\|_4~"phR.{/¡i>ryctjϵP{rƥihܓƵTNQ/oLq/MkI$˦1tv: S]+pf &DgbFVy:C,)Gzim~ <"rdtlEt@c# 2l<5^w#`i4%4.5VLI -ҔK\"5/n0:@Z뻙n֦Kl)U;fJ`y[Iy_K$iC$ᠳMt) `{f{afˁ)FGYeuxMZg\Q3\N OtT#?)N\%2aCWpll :F4.50:`\. E"7 _[Qt0s[R.;KFǓR>@3] jڵ9j˷oEw̘\ߜsg'm7J$MֶAsOi7:bV\ <O`0%)]c2)K'0Z%Ͳ/M\"\Nݡ Z <7rfk{,LY*I E_EU76x.ǵ)}CT[Ny)+#[J\\+ySyҸ4򇖭u)Mpiy[еI46.Ibt3ήAg K8@n*r ]Srx,6jv ]7HNx:nٵW)!R\P{uxƬ-5'H<ht4ߌ˪;Ύj\)Z):N83rctœƥˢ&C3-)w:4.Ids 'FǨ]R.g"Mj)߼x ( `7F'ܢJ|89E+)CR.7/cse. lpVm{[jƃ7{5gtT7# r@S-R xhgZDwC㪫Z뻹tQ{\Kx c<&2Ә^H|x.UhW.pdtR./IF9Trӳn89:6I0xb +Ig N':Hr`ivКR.+S.opVm+)w\4l܅I:; mc) ,OoL F^l OcR./ u|rytwFw̨\rBOcV[ޖZSQ-} IDAT>6Hhcν5Rr\YҜr>:BZYE]tThD]R.R <&rB*NRE_W$i8t:wQO~_GH}7M5r17EyGH[q)ק\\hTG۵fUk\N\#ק\\J\ry%i)#C$: >elr9upVmu~g6踔˵Z\e__ {8F7FQ7)g0XjmS粚SҺ\z ͡q ~Ps>)hSe.r$6pybt/rm=LGSR.EhƋ>ԆEHq2)]LH<xn)Ԕ1Ry:\~< Uk\Nf;*:@6@;-<\ <9rytHۥ\x  j;[ʂlR.WFw4;Uw'G4w$K0'*oAg5_xFfދUOJ\I\r]T//r'r}MÁna5p)c{z\~-lrT2:@ooB'oE~_H2+R.7\>h{NrYܣv9;i_qZ5.3ωP;\QsJ/2/V If1jˊJtfڡ=]R._x+pepfەSeut)\<8=G38!)S.EH g29yܛ“fz0sIN8:GO2R.\^ l |цRdb)kj/ _R.+#x)qf)nvpCtZi]r9aR_e%IRl 1tv: 5[C]?hvO$rct&/r]=fFN^zjK聈4M/H<*nNN}{*F{uS.Ĭ6-ۡZYc]s\vI$X)GHGh&848ߍn&m+Mүl3㳣;N^ n\r,U4pp__$*wᠳMt-rSV0xf!^岔n?ST9K){,2rpϪލN:ENr``~R.O.nik}۔{~$foV1:S rK|{GEhfxp+!ZSԔӁGGGhr>rFtfC ~t4C`etD5qUݖCE0:^)Dw ]ֈUrOҡe~ x$Ij'Cݢcl)S.9GuȔףC4])sS./ftj7sN?)ociI\ry' To2:)kc\ \+7}ytb{w*иT__Nߟ~"͌}G\<R.ry ~ٲfH<-rbtf#iN){RCGw4%I5kVOO8:d ~ 83:"Pry{ʥuU4)$IR:AgᠳIt-rf?Qkr99:FqƧ6l< oi2SS.MfV˫xJeY1RK94@)0ZXۺ 8*AetT1R=] !:BrI6TmrsNj˷mgJ70:y)ٕrYYi\0MEGh".gR.ߏn)끗4)gNЦi O#$I9~ 'FǨR.[me&lJry$:͹\"rVٌ>7[:xM#u#(M`莚Yh}R.;\ڴ SvǥzzcSZ(rU}F'r0:H rKÀݤVpܨS5P]3^7|,:BsBt42?%:fZR.EwR.G1%WriUsW/$I>7/[>C&-x{/vc4WoJ8{|v/[Q%E'^;VrqUO\>u xGtK9;R.gDTSvǥ;U+r.~AIjoN<:Dr~@`w`9p*5AS.DHk]FϑaJxX; 2z~ &LF}e~F;!>rx/ޙ~3_H$in>{kc^ьǷÛjZ9 oARu] Q}\\NމzM w!+]t^C|E_ޞr)!UO*txu!MjF':Du _tcϩ\~ܢ)o%s ?reEt.^πKGU{_ON|+:BuO!M<(:-muum"c)FLeh`8 ,ʋI$Mve˧KufxsJKuo<'̮_1ZL1R{ݍ+Mpbr95:FR.{GGE};0K=ޚrYt4{*tlʥ> 9x f[O<48Gu.pFϏ{ݻ2)9wXki;j[)q kf)kk[~xmt4O7gg7V2߀~VF։oؾ_z$ItwFs2t^ld)Ӂ0J=b `) c)UW{0>0 V>r^t6)/{G3Z QhP)_HǀWNP^R#ϴoK2ew*{\u \nQׁ{c4<(JF/JQ/ǍgYǁCS.GHqh|C)|ЁVzp>8X+o79~/rc Stf>?.cܯN y p$Ij9˖>:Fu7bCg[ia!)k[4]mP 9KOO#͒~_+mv) .Zx<-6> l2AÀR.?učn\&:b]v>Y;MI`!P&Y봛4I ה^$LAIi?i@ߟEՎsh{߻p>?~w'yu ghCd-%"on9?#VÚ$Q'K0zIi?$)V%DM/ᭅ{F+vd3'dhM$W0z 3i/>#^M$JG=LC+fc g0t̬O`&swyKnj^$tÝ#bh {(|cA.hg'EX.*Rfz&i+~*ɣqv%lO{`E_Kcc#hs2K`L^sB&?)"ug;65;6}?:G5ni \elNrq \Me&G$o.qwݼmcfUƓda~nMǯ[^sj珎´חu;$ωrII;(`=au;JsiȟX8[}I7aNMr%yPrE/Y^s$w3Ie\GKu;v^8bWG]{.=_9]{zmJrpJ]M;@LNZw]#I>i&Vfcq%b=zqI.I[/v>;ȗa `g$tJIZ;r;[r]LIZo%U}P=;ͥcn7NK#I_4mIK;9 *G<0/I$-[4nK(Ϳ&90zNvx|Wyc~n R']~ݙvǕI.bnIItvRyG0bqv'CJw ?n/^KG)M}pϺ v|ɋ<-57>.eenIiǟ䩙($ẃ7ڍI[e |$}ʹgjg݅I^ o-u;M2,c \|-^ܖH`ˠIn7l[vV* cf%yf7QK3NfIٴoMvM򒴻۽a\v7'9IG^us>ϺzmMcI> -Ku;$yD(?5P:dHҶ'ut2:nvm]L4!zΉI8ɯd<{uw%yW0t k'$)I+5[$_O;$_휔$U٢-2Wv~2YiΪM*0mz-MH9-n?Ue { ѦVI{wlL2H;(op*>[$Y'y?0JUus~fO9!]Y}$ǔpI.IF3I8hKKFI)IO'p0pPb =7ɓu\i? .n4d~R$^vδ -lMe+W^I~'W$%$P"ɞ!ϻӍI.Krq!/{$u;}=D]eʝi9/ɹi@v0zKIpj{k0Rz}, 笶OKpGVFy\'yik&ܦ컃1vvYْ$vsm+XӞD}I\-cm=Cǎ){^f'qW99sɓ:\|-iK.HrvRF'Ɵ=C[ic?pu;$$N|I>vu+XL^󐴋Ǵ; ?!Ņ-5nK;0,B0hh?9ÊFM&3 ~^տ(&\yN$ vN5I C`~y|պ$3{nAi=+I>Emi$W>s|;k#9iwp0ܴ?{lOs^@kc$9+-26FKnV˶OI{`qpdzòS?sw$f΢UiSumOri}]Ef0 <(䎴+$|? pPn縴m"}$L[m\I.Ϟ[K8u;!?9ɃK6vWg$ (ZS<*-[lKI>fUyhg2ȬO'I?n)@YnI=C?)Mof z?i,n)94cI>Yۚ$QՍJ*q͞ANrjSF#R =~ua@$|cJv-I̞a\ Z9&;d [L{{I0^ ou;OJ;`_]3=Ox̺IMrv 0ziw?=av]irlʌfߣ'i>v=z^ǚ9nņƓda~nv$$!p4z΃g|itޕ qo7fvܞVpx< ߝƴx Ona3@EGc &ɺ{W$`G'ɉI6Ý{`oS0C~u8~ֵ3ξ?Ӻ 4ec^Kb[gao?Y.8ѐnѱ %+|6r_[&u;O=v!&F/l0T6 IvnF];;I/&V[)I=$+0'ygVut х8'{ߵIg`xJV$J;D˽^T)͹`7u0iF"?){x{/@ IDATj~w^Tѱa`xJrzI3`?0B'~nmtlv]vI o^&̤^sB>9\n^In! 휘lG}\uIޘ䬪.ivWqi7wl`e5.0M'ܛq$!x$>L&[ O3t٘-IάfGcJ+b $oK2\1@cx,'$!Ɠ&܋,, s,i`ٍNIS$Hrc3n;dMǖn"M'ySU7nX '/'Shc$9cqL%\]:` =(ے ~t $}~S34^$o(Qٞե#`LxTu$+Ӫn(dG~3Ɏo$0inh5IQUl-fGGKGpOI4C*%ISژ#`RMxTu$Wn֕I5C# ɻKG$nL,i1ɫU:&T'IU7dP{n.,nG^dC$_:L WusS)FU7KG4&|ɪnKGn$dkvWV: 'IU7W&yKnY:L YKJG̠Wu7#`xU7;$[f$tLOn.H3UU\:L~KG̀Vuϥ#`ZxU7["-S$-lfƓtjn,lGޑBQͿi7CU4I~1-S$o,`ƓJ)3+R:fnI[` JG04't;?Ybh|/UܕIvn@[C`OU7$wYS:fiϗ $0暦)0$.0nJnn)NPI~tۙC%@1nX:f\4܉I.L-,HnVrddæ~'qߙ҅0>\KKG34~$_Mb`ݲ>ښ[gs\':e`]Ul(!Z{Y*cSr%ɺoڟK~ I 5Wu!@rLIQ͇t[~Ɠi U+u`|?<䳥#8tW\|d#Ͽ˪Q:80CGl-/[ǹʵXPbƘePgnYv x|=ٱs xmU7甎2I>T`-.&^ekrUUW:84Ɨ/'tY6ټewxurWDž$Y:8tƗQU7w%yQJ̒+-󸋋9\U7J2憴K̂HX>֝MI^ٹXb_w$]QInvUR$/L2fLn)]+MRխI^T͆!04FLt K۵+^]hkTusm`_eUݜ4[&][;785&۶{XLʪnR:X^ _vIu o}4I~.,?CTu$.0o(]pp#Y\,]n04^|t}ƷnO֬-]IQͻJG+xAU,&yEn$04$ë)]{gU7X:XY fg%TImGyKCKW0.oV1PͶ$$/0&e.t3j>ɯV`UlI$_)06l*]pxnߐpK f']Cah|Tu9ɏ't {[gqVtU7;JfC't 8{k÷[mw`\'R:X]PU7<7ɕ[$'vg]ǪX:X}TU7$yNon':4v]as 7A(vfخ e|l\ (C <+M[V44$k6Α"}@U\I))if-eZv߲5L <[Kf]3_`5LNIfm&yISCeh|Uu>s|p ;[`}.P:oƧ@U7 ٥[V)]|&nاͥCgh|JTu5I7?nX);EZch}$KR: S4zGU7;B.R;Eo;$?SͶ!04>eUͫip}K,/\U7;JwtrƓddw&nK%$Mr)i|׮些KWPЮ$ U*L&CSw'$‰(]֮+]@!w'yiU7Y:lg@U7yIn/p[+Xe7'9&Q<-ɕ[ƃph|q1˓< J $OO-G'.Xkו.`|63Y[:gLU7'y~n8'/9ﺛ)] ;+ɏUusg`AUl旒^#9遥 ߶KWRMCch|UuI^dk.Xkו.`H򟫺Ceh|Uu$NrkCȇ.XƧΆ$/CfhTu$?dt 8+c +X&%yFU7V:~ITu6't9~I,]26>.Jn, C|[U7({J̴6nh|}03$j nvVu$oHt< VwHn^Y͖1l14Βyg$t Ryrr\銕qͥ 8L7'Ѫnt0 s@Uݜ)I.*<+;JpMnT:]ƹWU\䇓M=;KJp*ɳYW:mǕ`Uu-ɯ-}){P8 ISlVdymؔ,.&[ؒUUK$v0TusVJt @sLGX~+8k<80N sX,S|t @<;KJO'yJU7_+7Cn6WuI^d{`=A)+]Y4I8 t sĪ$Jr]`=K,?;I~߫fWTu$ON-:aɣO)]nߘjJW̼'nt14Qf}&$vaxj͕X>ɆM+f{^͚!{8J"XI'M u(fߍmn9[d 3s#!1lf7: S \81q,_Y)r]4=??^NΘ2u:5'ٷt 0y9Y}銩'^SbIrn_,\gJMMk`sX2{v銩$Gh)W7INN$  ZQblxtx:^7cvV}XV+|6[񍛓{/]-MN?tػ+/M[]8Ӫn;cI& G&}l.]02ɱsiӵ I>dy`=>7F{xd䬓JWGZT:`*4Ό$G$t`-^:&5¿骪tX28qi":3eIn'_h^?䄣JW&PQ:`UFY_I"ɗJm_̛[dG邱q{c qe4N1u?Z7[3ɺ=ZWrƉKv>Kg|4OMߕ.U ][T_L!Yt[gr$ﮛ!ͥqB%9%yI6Ԣ(]:tz2&Yi0L :][$'nӦgoݑ}_> +IutL2g(umU%yg]8SIn5Yt6 'o99YtHYumj][pI58ڲ%cure[*91_H[u?\:qFBVUKiɿݖ|xc_UGn(hѵ$%9?ɏ>gΟaeQ& IDAT3#OI~nuc8#kI>M[%ޟl4}sвdd1Iέ!hյ%DJk'G?/9UI.#NrQ?~cac4HjQ$y_مs1~C#cu;x١$fMor_]:`X3:*%I+L}*Y`DdΜddmMi(Hut3gltm5+{\d9Go\:`3vZIޝdNvЦ$L(0J[][I.K$M!h׵ՉI>desgߩ!hѵ'0kKL$ ɥuo.0ƙ(][KIOwI)ɧi􏗎FLHI>dAI$W7=Cƍ8keI<ےTsѕI.!htmud'yC1qMfqg4ѵ\-# זF<][JrN?JrxQ R:`ËڪJ$9$M!hv@Vgd0_Y`HܙO|ng$q ][x|UBN$W q][$n!w'0\:ma7tm2gn&&8W]NF0:2[Tsv$m릿t /hPV'$$]8`gO$1q][IޞdQ@O&n'Jsaum(;fCے\7` ڪJrZ'ySYe wU릿thfXV?Iޕdq`rlL w`C!][-L$Kp0$~M8 NJ$oN2p0J&nK0atm,{dI`ܞUMxf8 '9'ɻ*[ I.Ori.3!׵$Wp0 image/svg+xml cookiecutter-1.3.0/setup.cfg000066400000000000000000000003301262047200400160250ustar00rootroot00000000000000[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 cookiecutter-1.3.0/setup.py000077500000000000000000000053431262047200400157320ustar00rootroot00000000000000#!/usr/bin/env python import os import sys try: from setuptools import setup except ImportError: from distutils.core import setup version = "1.3.0" if sys.argv[-1] == 'publish': os.system('python setup.py sdist upload') os.system('python setup.py bdist_wheel upload') sys.exit() if sys.argv[-1] == 'tag': os.system("git tag -a %s -m 'version %s'" % (version, version)) os.system("git push --tags") sys.exit() with open('README.rst') as readme_file: readme = readme_file.read() with open('HISTORY.rst') as history_file: history = history_file.read().replace('.. :changelog:', '') requirements = [ 'future>=0.15.2', 'binaryornot>=0.2.0', 'jinja2>=2.7', 'click>=5.0', 'whichcraft>=0.1.1' ] long_description = readme + '\n\n' + history if sys.argv[-1] == 'readme': print(long_description) sys.exit() setup( name='cookiecutter', version=version, description=('A command-line utility that creates projects from project ' 'templates, e.g. creating a Python package project from a ' 'Python package project template.'), long_description=long_description, author='Audrey Roy', author_email='audreyr@gmail.com', url='https://github.com/audreyr/cookiecutter', packages=[ 'cookiecutter', ], package_dir={'cookiecutter': 'cookiecutter'}, entry_points={ 'console_scripts': [ 'cookiecutter = cookiecutter.cli:main', ] }, 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=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', 'Natural Language :: English', 'License :: OSI Approved :: BSD License', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development', ], keywords=( 'cookiecutter, Python, projects, project templates, Jinja2, ' 'skeleton, scaffolding, project directory, setup.py, package, ' 'packaging' ), ) cookiecutter-1.3.0/tests/000077500000000000000000000000001262047200400153525ustar00rootroot00000000000000cookiecutter-1.3.0/tests/__init__.py000066400000000000000000000000001262047200400174510ustar00rootroot00000000000000cookiecutter-1.3.0/tests/conftest.py000066400000000000000000000116101262047200400175500ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ conftest -------- Contains pytest fixtures which are globally available throughout the suite. """ import pytest import os import shutil from cookiecutter import utils def backup_dir(original_dir, backup_dir): # If the default original_dir is pre-existing, move it to a temp location if not os.path.isdir(original_dir): return False # Remove existing backups before backing up. If they exist, they're stale. if os.path.isdir(backup_dir): utils.rmtree(backup_dir) shutil.copytree(original_dir, backup_dir) return True def restore_backup_dir(original_dir, backup_dir, original_dir_found): # Carefully delete the created original_dir only in certain # conditions. original_dir_is_dir = os.path.isdir(original_dir) if original_dir_found: # Delete the created original_dir as long as a backup # exists if original_dir_is_dir and os.path.isdir(backup_dir): utils.rmtree(original_dir) else: # Delete the created original_dir. # There's no backup because it never existed if original_dir_is_dir: utils.rmtree(original_dir) # Restore the user's default original_dir contents if os.path.isdir(backup_dir): shutil.copytree(backup_dir, original_dir) if os.path.isdir(original_dir): utils.rmtree(backup_dir) @pytest.fixture(scope='function') def clean_system(request): """ Fixture that simulates a clean system with no config/cloned cookiecutters. It runs code which can be regarded as setup code as known from a unittest TestCase. Additionally it defines a local function referring to values which have been stored to local variables in the setup such as the location of the cookiecutters on disk. This function is registered as a teardown hook with `request.addfinalizer` at the very end of the fixture. Pytest runs the named hook as soon as the fixture is out of scope, when the test finished to put it another way. During setup: * Back up the `~/.cookiecutterrc` config file to `~/.cookiecutterrc.backup` * Back up the `~/.cookiecutters/` dir to `~/.cookiecutters.backup/` * Back up the `~/.cookiecutter_replay/` dir to `~/.cookiecutter_replay.backup/` * Starts off a test case with no pre-existing `~/.cookiecutterrc` or `~/.cookiecutters/` or `~/.cookiecutter_replay/` During teardown: * Delete `~/.cookiecutters/` only if a backup is present at `~/.cookiecutters.backup/` * Delete `~/.cookiecutter_replay/` only if a backup is present at `~/.cookiecutter_replay.backup/` * Restore the `~/.cookiecutterrc` config file from `~/.cookiecutterrc.backup` * Restore the `~/.cookiecutters/` dir from `~/.cookiecutters.backup/` * Restore the `~/.cookiecutter_replay/` dir from `~/.cookiecutter_replay.backup/` """ # If ~/.cookiecutterrc is pre-existing, move it to a temp location user_config_path = os.path.expanduser('~/.cookiecutterrc') user_config_path_backup = os.path.expanduser( '~/.cookiecutterrc.backup' ) if os.path.exists(user_config_path): user_config_found = True shutil.copy(user_config_path, user_config_path_backup) os.remove(user_config_path) else: user_config_found = False # If the default cookiecutters_dir is pre-existing, move it to a # temp location cookiecutters_dir = os.path.expanduser('~/.cookiecutters') cookiecutters_dir_backup = os.path.expanduser('~/.cookiecutters.backup') cookiecutters_dir_found = backup_dir( cookiecutters_dir, cookiecutters_dir_backup ) # If the default cookiecutter_replay_dir is pre-existing, move it to a # temp location cookiecutter_replay_dir = os.path.expanduser('~/.cookiecutter_replay') cookiecutter_replay_dir_backup = os.path.expanduser( '~/.cookiecutter_replay.backup' ) cookiecutter_replay_dir_found = backup_dir( cookiecutter_replay_dir, cookiecutter_replay_dir_backup ) def restore_backup(): # If it existed, restore ~/.cookiecutterrc # We never write to ~/.cookiecutterrc, so this logic is simpler. if user_config_found and os.path.exists(user_config_path_backup): shutil.copy(user_config_path_backup, user_config_path) os.remove(user_config_path_backup) # Carefully delete the created ~/.cookiecutters dir only in certain # conditions. restore_backup_dir( cookiecutters_dir, cookiecutters_dir_backup, cookiecutters_dir_found ) # Carefully delete the created ~/.cookiecutter_replay dir only in # certain conditions. restore_backup_dir( cookiecutter_replay_dir, cookiecutter_replay_dir_backup, cookiecutter_replay_dir_found ) request.addfinalizer(restore_backup) cookiecutter-1.3.0/tests/fake-repo-bad/000077500000000000000000000000001262047200400177475ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo-bad/no-project-in-here.txt000066400000000000000000000000111262047200400241050ustar00rootroot00000000000000Ha ha ha!cookiecutter-1.3.0/tests/fake-repo-pre/000077500000000000000000000000001262047200400200075ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo-pre/cookiecutter.json000066400000000000000000000004271262047200400234050ustar00rootroot00000000000000{ "full_name": "Audrey Roy", "email": "audreyr@gmail.com", "github_username": "audreyr", "project_name": "Fake Project", "repo_name": "fake-project", "project_short_description": "This is a fake project.", "release_date": "2013-07-28", "year": "2013", "version": "0.1" }cookiecutter-1.3.0/tests/fake-repo-pre/{{cookiecutter.repo_name}}/000077500000000000000000000000001262047200400254335ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo-pre/{{cookiecutter.repo_name}}/README.rst000066400000000000000000000000611262047200400271170ustar00rootroot00000000000000============ Fake Project ============ Blah!!!! cookiecutter-1.3.0/tests/fake-repo-pre2/000077500000000000000000000000001262047200400200715ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo-pre2/cookiecutter.json000066400000000000000000000004271262047200400234670ustar00rootroot00000000000000{ "full_name": "Audrey Roy", "email": "audreyr@gmail.com", "github_username": "audreyr", "project_name": "Fake Project", "repo_name": "fake-project", "project_short_description": "This is a fake project.", "release_date": "2013-07-28", "year": "2013", "version": "0.1" }cookiecutter-1.3.0/tests/fake-repo-pre2/whatever.some.thing000066400000000000000000000000001262047200400237010ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo-pre2/{{cookiecutter.repo_name}}/000077500000000000000000000000001262047200400255155ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo-pre2/{{cookiecutter.repo_name}}/README.rst000066400000000000000000000000611262047200400272010ustar00rootroot00000000000000============ Fake Project ============ Blah!!!! cookiecutter-1.3.0/tests/fake-repo-tmpl/000077500000000000000000000000001262047200400201755ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo-tmpl/cookiecutter.json000077500000000000000000000005151262047200400235740ustar00rootroot00000000000000{ "full_name": "Audrey Roy", "email": "audreyr@gmail.com", "github_username": "audreyr", "project_name": "Fake Project Templated", "repo_name": "{{ cookiecutter.project_name|lower|replace(' ', '-') }}", "project_short_description": "This is a fake project.", "release_date": "2013-07-28", "year": "2013", "version": "0.1" } cookiecutter-1.3.0/tests/fake-repo-tmpl/{{cookiecutter.repo_name}}/000077500000000000000000000000001262047200400256215ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo-tmpl/{{cookiecutter.repo_name}}/README.rst000077500000000000000000000000611262047200400273100ustar00rootroot00000000000000============ Fake Project ============ Blah!!!! cookiecutter-1.3.0/tests/fake-repo/000077500000000000000000000000001262047200400172235ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo/fake-project/000077500000000000000000000000001262047200400215755ustar00rootroot00000000000000cookiecutter-1.3.0/tests/fake-repo/fake-project/README.rst000066400000000000000000000000611262047200400232610ustar00rootroot00000000000000============ Fake Project ============ Blah!!!! cookiecutter-1.3.0/tests/files/000077500000000000000000000000001262047200400164545ustar00rootroot00000000000000cookiecutter-1.3.0/tests/files/syntax_error.txt000066400000000000000000000000671262047200400217570ustar00rootroot00000000000000I eat {{ syntax_error }} {# this comment is not closed}cookiecutter-1.3.0/tests/files/unicode.txt000066400000000000000000000001301262047200400206350ustar00rootroot00000000000000Polish: Ą Ł Ż Chinese: 倀 倁 倂 倃 倄 倅 倆 倇 倈 Musical Notes: ♬ ♫ ♯cookiecutter-1.3.0/tests/files/{% if generate_file == 'y' %}cheese.txt{% endif %}000066400000000000000000000000621262047200400267530ustar00rootroot00000000000000Testing that generate_file was {{ generate_file }}cookiecutter-1.3.0/tests/files/{{generate_file}}.txt000066400000000000000000000000331262047200400227620ustar00rootroot00000000000000Testing {{ generate_file }}cookiecutter-1.3.0/tests/hooks-abort-render/000077500000000000000000000000001262047200400210575ustar00rootroot00000000000000cookiecutter-1.3.0/tests/hooks-abort-render/hooks/000077500000000000000000000000001262047200400222025ustar00rootroot00000000000000cookiecutter-1.3.0/tests/hooks-abort-render/hooks/post_gen_project.py000066400000000000000000000002501262047200400261150ustar00rootroot00000000000000#!/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 %} cookiecutter-1.3.0/tests/hooks-abort-render/hooks/pre_gen_project.py000066400000000000000000000002471262047200400257240ustar00rootroot00000000000000#!/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 %} cookiecutter-1.3.0/tests/hooks-abort-render/{{cookiecutter.repo_dir}}/000077500000000000000000000000001262047200400263415ustar00rootroot00000000000000cookiecutter-1.3.0/tests/hooks-abort-render/{{cookiecutter.repo_dir}}/README.rst000066400000000000000000000000661262047200400300320ustar00rootroot00000000000000{{cookiecutter.repo_name}} ========================== cookiecutter-1.3.0/tests/replay/000077500000000000000000000000001262047200400166465ustar00rootroot00000000000000cookiecutter-1.3.0/tests/replay/conftest.py000066400000000000000000000013271262047200400210500ustar00rootroot00000000000000import pytest from cookiecutter import config @pytest.fixture def context(): """Fixture to return a valid context as known from a cookiecutter.json.""" return { u'cookiecutter': { u'email': u'raphael@hackebrot.de', u'full_name': u'Raphael Pierzina', u'github_username': u'hackebrot', u'version': u'0.1.0', } } @pytest.fixture def replay_test_dir(): return 'tests/test-replay/' @pytest.fixture def mock_user_config(mocker, replay_test_dir): user_config = config.DEFAULT_CONFIG user_config.update({'replay_dir': replay_test_dir}) return mocker.patch( 'cookiecutter.replay.get_user_config', return_value=user_config ) cookiecutter-1.3.0/tests/replay/test_dump.py000066400000000000000000000056411262047200400212320ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test_dump --------- """ import json import os import pytest from cookiecutter import replay @pytest.fixture def template_name(): """Fixture to return a valid template_name.""" return 'cookiedozer' @pytest.fixture def replay_file(replay_test_dir, template_name): """Fixture to return a actual file name of the dump.""" file_name = '{}.json'.format(template_name) return os.path.join(replay_test_dir, file_name) @pytest.fixture(autouse=True) def remove_replay_dump(request, replay_file): """Remove the replay file created by tests.""" def fin_remove_replay_file(): if os.path.exists(replay_file): os.remove(replay_file) request.addfinalizer(fin_remove_replay_file) def test_type_error_if_no_template_name(context): """Test that replay.dump raises if the tempate_name is not a valid str.""" with pytest.raises(TypeError): replay.dump(None, context) def test_type_error_if_not_dict_context(template_name): """Test that replay.dump raises if the context is not of type dict.""" with pytest.raises(TypeError): replay.dump(template_name, 'not_a_dict') def test_value_error_if_key_missing_in_context(template_name): """Test that replay.dump raises if the context does not contain a key named 'cookiecutter'. """ with pytest.raises(ValueError): replay.dump(template_name, {'foo': 'bar'}) @pytest.fixture def mock_ensure_failure(mocker): return mocker.patch( 'cookiecutter.replay.make_sure_path_exists', return_value=False ) @pytest.fixture def mock_ensure_success(mocker): return mocker.patch( 'cookiecutter.replay.make_sure_path_exists', return_value=True ) def test_ioerror_if_replay_dir_creation_fails( mock_ensure_failure, mock_user_config, replay_test_dir): """Test that replay.dump raises when the replay_dir cannot be created.""" with pytest.raises(IOError): replay.dump('foo', {'cookiecutter': {'hello': 'world'}}) mock_ensure_failure.assert_called_once_with(replay_test_dir) def test_run_json_dump(mocker, mock_ensure_success, mock_user_config, template_name, context, replay_test_dir, replay_file): """Test that replay.dump runs json.dump under the hood and that the context is correctly written to the expected file in the replay_dir. """ spy_get_replay_file = mocker.spy(replay, 'get_file_name') mock_json_dump = mocker.patch('json.dump', side_effect=json.dump) replay.dump(template_name, context) assert mock_user_config.call_count == 1 mock_ensure_success.assert_called_once_with(replay_test_dir) spy_get_replay_file.assert_called_once_with(replay_test_dir, template_name) assert mock_json_dump.call_count == 1 (dumped_context, outfile_handler), kwargs = mock_json_dump.call_args assert outfile_handler.name == replay_file assert dumped_context == context cookiecutter-1.3.0/tests/replay/test_load.py000066400000000000000000000034771262047200400212110ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test_load ----------- """ import json import os import pytest from cookiecutter import replay @pytest.fixture def template_name(): """Fixture to return a valid template_name.""" return 'cookiedozer_load' @pytest.fixture def replay_file(replay_test_dir, template_name): """Fixture to return a actual file name of the dump.""" file_name = '{}.json'.format(template_name) return os.path.join(replay_test_dir, file_name) def test_type_error_if_no_template_name(): """Test that replay.load raises if the tempate_name is not a valid str.""" with pytest.raises(TypeError): replay.load(None) def test_value_error_if_key_missing_in_context(mocker): """Test that replay.load raises if the loaded context does not contain 'cookiecutter'. """ with pytest.raises(ValueError): replay.load('invalid_replay') def test_io_error_if_no_replay_file(mocker, mock_user_config): """Test that replay.load raises if it cannot find a replay file.""" with pytest.raises(IOError): replay.load('no_replay') def test_run_json_load(mocker, mock_user_config, template_name, context, replay_test_dir, replay_file): """Test that replay.load runs json.load under the hood and that the context is correctly loaded from the file in replay_dir. """ spy_get_replay_file = mocker.spy(replay, 'get_file_name') mock_json_load = mocker.patch('json.load', side_effect=json.load) loaded_context = replay.load(template_name) assert mock_user_config.call_count == 1 spy_get_replay_file.assert_called_once_with(replay_test_dir, template_name) assert mock_json_load.call_count == 1 (infile_handler,), kwargs = mock_json_load.call_args assert infile_handler.name == replay_file assert loaded_context == context cookiecutter-1.3.0/tests/replay/test_replay.py000066400000000000000000000036511262047200400215600ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test_replay ----------- """ import os import pytest from cookiecutter import replay, main, exceptions def test_get_replay_file_name(): """Make sure that replay.get_file_name generates a valid json file path.""" exp_replay_file_name = os.path.join('foo', 'bar.json') assert replay.get_file_name('foo', 'bar') == exp_replay_file_name @pytest.fixture(params=[ {'no_input': True}, {'extra_context': {}}, {'no_input': True, 'extra_context': {}}, ]) def invalid_kwargs(request): return request.param def test_raise_on_invalid_mode(invalid_kwargs): with pytest.raises(exceptions.InvalidModeException): main.cookiecutter('foo', replay=True, **invalid_kwargs) def test_main_does_not_invoke_dump_but_load(mocker): mock_prompt = mocker.patch('cookiecutter.main.prompt_for_config') mock_gen_context = mocker.patch('cookiecutter.main.generate_context') mock_gen_files = mocker.patch('cookiecutter.main.generate_files') mock_replay_dump = mocker.patch('cookiecutter.main.dump') mock_replay_load = mocker.patch('cookiecutter.main.load') main.cookiecutter('foobar', replay=True) assert not mock_prompt.called assert not mock_gen_context.called assert not mock_replay_dump.called assert mock_replay_load.called assert mock_gen_files.called def test_main_does_not_invoke_load_but_dump(mocker): mock_prompt = mocker.patch('cookiecutter.main.prompt_for_config') mock_gen_context = mocker.patch('cookiecutter.main.generate_context') mock_gen_files = mocker.patch('cookiecutter.main.generate_files') mock_replay_dump = mocker.patch('cookiecutter.main.dump') mock_replay_load = mocker.patch('cookiecutter.main.load') main.cookiecutter('foobar', replay=False) assert mock_prompt.called assert mock_gen_context.called assert mock_replay_dump.called assert not mock_replay_load.called assert mock_gen_files.called cookiecutter-1.3.0/tests/skipif_markers.py000066400000000000000000000011231262047200400207320ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ skipif_markers -------------- Contains pytest skipif markers to be used in the suite. """ import pytest import os try: os.environ[u'TRAVIS'] except KeyError: travis = False else: travis = True try: os.environ[u'DISABLE_NETWORK_TESTS'] except KeyError: no_network = False else: no_network = True skipif_travis = pytest.mark.skipif( travis, reason='Works locally with tox but fails on Travis.' ) skipif_no_network = pytest.mark.skipif( no_network, reason='Needs a network connection to GitHub/Bitbucket.' ) cookiecutter-1.3.0/tests/test-config/000077500000000000000000000000001262047200400175745ustar00rootroot00000000000000cookiecutter-1.3.0/tests/test-config/invalid-config.yaml000066400000000000000000000002421262047200400233470ustar00rootroot00000000000000default_context full_name: email: "firstname.lastname@gmail.com" github_username: "example" cookiecutters_dir: "/home/example/some-path-to-templates" cookiecutter-1.3.0/tests/test-config/valid-config.yaml000066400000000000000000000003561262047200400230260ustar00rootroot00000000000000default_context: full_name: "Firstname Lastname" email: "firstname.lastname@gmail.com" github_username: "example" cookiecutters_dir: "/home/example/some-path-to-templates" replay_dir: "/home/example/some-path-to-replay-files" cookiecutter-1.3.0/tests/test-config/valid-partial-config.yaml000066400000000000000000000001761262047200400244600ustar00rootroot00000000000000default_context: full_name: "Firstname Lastname" email: "firstname.lastname@gmail.com" github_username: "example" cookiecutter-1.3.0/tests/test-generate-binaries/000077500000000000000000000000001262047200400217135ustar00rootroot00000000000000cookiecutter-1.3.0/tests/test-generate-binaries/input{{cookiecutter.binary_test}}/000077500000000000000000000000001262047200400307755ustar00rootroot00000000000000cookiecutter-1.3.0/tests/test-generate-binaries/input{{cookiecutter.binary_test}}/.DS_Store000066400000000000000000000360041262047200400324630ustar00rootroot00000000000000Bud1   Storedi  @ @ @ @ .DS_Storedilcblob >v1b682px-Oscypki.jpgdilcblob vze1bzcheeseland-notes.txtdilcblob v#1b{cookiecutter_todo.txtdilcblob rv1b|#Empty Tunnelblick VPN Configurationbwspblobbplist00  \WindowBounds[ShowSidebar]ShowStatusBar[ShowPathbar[ShowToolbar\SidebarWidth_{{408, 292}, {770, 438}}  ".v\) inspirationdilcblob ;"1b} resourcesbwspblobbplist00 \WindowBounds[ShowSidebar]ShowStatusBar[ShowPathbar[ShowToolbar\SidebarWidth_{{446, 338}, {770, 438}} ". VS`i̯ 8Rnkk22;A!.΁YϪV!}{G,Zy':jT:S@^p=BrlHL`(*RX ϺaL~O>P@ GG;M'<: A7z Oq/\?) c[ 'P l^Q^([&` ܥc[~ !Ӯ/ulJwʲOB2}Ixt]"m\8Rd^ E+fJXԡg5Jn5ql3` 5"K%pc[/]Q@`:0*w8z+1uW̑g?zO"F[q*(as;pppN6k>78 uN-pl+pp-)8T ] 3"vm L7юm%owFrbNul$zWW&(#z z|bdCR4 '>b/\Lj xuG nSu ,!^pűiplk0!9^wl; D_5o泌$9e; '8u p#тd8j2 QSaCj2?)dzpZѱ-AS3`헰~,tlkb>1 D :1`P5 c֥0br)Z= 8%dSQֱ oF TRbt ݿp=&T!!zD' MYRQGqy(;ݎm[Y%:􎒱_x$s=0? ^C+P O]_pl30mg.pk&0`2SԹ8Y|13ehBan#Zw5Щ5[Q~ mc`a_֫D0A3slKpLPԸ/&y9put=n'F:xҥGGVbND]?#Ev=L%8P,F*tkle 6`|z~ЭL8c[P(f?%=U n{c[ձhhKIK?2+(%u=6=Оvj~# o::wR:m+9}-kR9ސ~WHpMGX[D 3 x4iQ@pp=Dc+Z37Ю<\k}ޡ4>(vu.74*ݼ >5ӵ@ߡeaD?Mm[Vn {QVm eüdHِ^s8 "sn*QI SuMONjdc[=1{3@ݰ BqtlJ!r]%#*@3&z(f\izEk+mx?I#@L6¬~JxU P!r}_rte4(BBS>Sb,?pl"ʵ=fP\Ůo5E#۸SnmE?6}h:Lh2/3+NN1E$rlkdeh;ٗ_1zsڀ)E~}?zO":hޤOxHv>‰z҃e(7֘4E 3KZD9R38+ڿ?5ӞZ%sh 'Yx{uWOzen2$cLw'iA+JCI#ӈk0Nb2Ɯԛql#fNubɱ4!iYzgupտQ\VXyP2Hq:iPmoʅ0>TU%.qRǶn^C~*PA\m-#3=>wjR.Wu(}ǶBYEEefHs3㕡b4 HmMAyP#ˁn.ȪW~'iE΋}DؖzB{ +$J\#.(7S8u!݃c'O!αE?03A}0mB\|81c[VW>g:5/"'H/Ƕ*ZGǁ2उޓul+JU ߏ5slnG / RqJ pDEG4=Ci_BWP4˅xO^Pw`S|c[SDFrn(p̯?73 iIXR=D_8Uc:=72Y2N-& <6CcWN >;v=m0A6&Ѿ=wA1OԌe4 );h=a#S;f߯rRݸS)z3XsgCrc45׺oʮIIY1-wcZ ݠ]wGpC >%Lz $\v=e (E 26mYg8ՃAE?B{DX]ʸb,Nu=.S\Ͽ5 @}(w 5Ԑ+x (XuUpM/&M+}M yӅXI(QS,A'_hJ"ikcW#dI%GJfǥלṨ.|/cU$m}{;@=K҃v }2|/s8PzNxnx1ǶFfj@~0 ";:Rh9nj8 m!um3?NOr׼'!>?{5 -.=0Z>uI}ZűA(0uU7T:Tuo I؈݆UT21{OQF]C[e2L VҾ!#}BVcZ l8##VơZ |yALZ9+[fdZ oellgBEHLZkvglassmusicsearchuni2709heartstarstaremptyuserfilmthlargeththlistokremovezoominzoomoutoffsignalcogtrashhomefiletimeroaddownloadaltdownloaduploadinboxplaycirclerepeatrefreshlistaltlockflagheadphonesvolumeoffvolumedownvolumeupqrcodebarcodetagtagsbookbookmarkprintcamerafontbolditalictextheighttextwidthalignleftaligncenteralignrightalignjustifylistindentleftindentrightfacetimevideopictureuni270FmapmarkeradjusttinteditsharecheckmovestepbackwardfastbackwardbackwardplaypausestopforwardfastforwardstepforwardejectchevronleftchevronrightplussignminussignremovesignoksignquestionsigninfosignscreenshotremovecircleokcirclebancirclearrowleftarrowrightarrowuparrowdownsharealtresizefullresizesmallexclamationsigngiftleaffireeyeopeneyeclosewarningsignplanecalendarrandomcommentsmagnetchevronupchevrondownretweetshoppingcartfolderclosefolderopenresizeverticalresizehorizontalhddbullhornbellcertificatethumbsupthumbsdownhandrighthandlefthandtophanddowncirclearrowrightcirclearrowleftcirclearrowtopcirclearrowdownglobewrenchtasksfilterbriefcasefullscreendashboardpaperclipheartemptylinkphonepushpinEurousdgbpsortsortbyalphabetsortbyalphabetaltsortbyordersortbyorderaltsortbyattributessortbyattributesaltuncheckedexpandcollapsecollapsetopJan KovarikGLYPHICONS Halflings-RegularGLYPHICONS Halflings{&07@FMQZ_fmqv&+8=KX]aft} ).28CLQU_dhlpuy "&+048<@FJNR[^dinsw|+ ! = * ,\% [ ! ou)t - \- - f "k" . p % V \. uo 4  * ^ T> ^] A 4'k c '>G ( 1 BB e ; U X  % 3 +3 5M uo 1 k1 % PK < )uto E S|M  J) ((} ST pHHpi \\ Iv|E Jq / J J J P\ 0 ¸ |JE |/ N~ pHHp : `4` ouR % Y \ T^ ^T T^^T 5 ` d\ r' $  \\ |Z  fg d S \ ^T c$N $c \~ \ PP \' $ \  $ \ v\wR \D sm { |  T \ $Q \Z -'V Tg c  h 'Y'$' (J \$ h \ \ Z 9  '\'g  U } J t  ' \ o <  '\ -ouuo d \\ !! w  '  ` ;%;-QE 7SWq'mBo t  7 9 . l @OyFUa F{  :Pf|!Tqp*Gr n<O`^#Gk8jU? 0 !~!!!"#J###$/$%-&&'9''(),))*3*****+$+b++ wR : Q 'N$|:/FUK?2n"i#ssEU}>>KV2n"i#sse.( )6@l m: Y: ' : $$| IMO^B}~B^ = P$= YT vX"ggU yb N¸%N 9999 T%'$}: $pp: \z $gz L L $$g\P L :|:" |$" |$" 9 Pou\R \,P%$9 .bchh78pp hhhhhhhhe.\s t\6V{l @ ''dff.( )e ; X @l .$|[ [ *Pou$Y g*C 7^ ] ^ S7 :p91g! 1gpv\Pw$< | \: $ \ \i{||Kjlm:`jn{~+ %+;:, $,~zܵjie;nkk,epttqss %,ej~l{njaiifn܆n{k}ke+ottrrs $ *+ + * P\ xw'}~Y  }7'\¸T]p p p p v]\Z $_b PQ }}  7$$t [ ?*[ ?2$h \j  }w'xC: d6eC $$>tdwZ P~ ~$$~_C[ *[ \2**t ***4~ **Pm o[P\Po Y\g Y +)#)6 ' ]\f Q &&F6/^ ] SS> *+ & ]?v[ '$'*w[ B%Z%B*@P$$%%@)+ *> mY@ %%v@?]**^ ] /6Eph : : |t'XP\XP\XDP\r'v|\ChN 1g 11'c SM G \**Y w x''\$\\$|w \|h: }}Y} PPi}}Y} h@OLO 3[y!!DD!!D!!r !!  [$F1).k5UFU޳ NbN:͒ _<ˑ%('Y#/hct \\g ޳ FVE1*.k4UGdj i' ''$!'TZ\t !'\  EU i|t \''%"'J N @`j'P\~ U 'Z@''@'@dAB''r'J $\J h g 0wW\\'\z\|||||k|\||||\| h g ''H0D'E0k@_ v&'Q ~_ HccLcd˲ʋcdKddv&&Q }_ kPPVVYeudcKdc˳ˋcdKcdv*|||1n tpVOVP  \ UcV}\y' xP\ P }}~e,~e,}}h_h: \U|T^ J*\(**\*`0P\\PP\ƺƺ\Pw {: 5֤w|HIQ%i21hmasã#zYaa] F]| DC.(MDt{@RYjY2/dF[ЋWW0 *? gB!(;&##h & !K\;Q =Rp{5AJ2\'R'v \\\VQ R.iȋ\zN y -Sj;@;;@H\R.iɋ\zN y .R);@;;@i Q<+|<|+<+<i ;;I N~) $) |$|) $/I N~* ///I h*# \# \# \# @ *O '+3 J@]']@:7v@ O '% 3 @\@;7 D 1ibbiibbiU vxswwsswxr0Ju|OAAOOAAO~~iQ $--ymzy(/jj55jjjjon7o2a60[sxk&gsTbT]`(((([ *(   Wl#qdy&U!({NfXyx+dH0X0 o6-_2 NNl8u| 005Y>A %Ѷ뢉8\-5e㐋A FEba_g 8=| :#''BClZWU \| ZU Z\| U WCZw C$Dv~P{ {D| w P{| n \xPkPn ouY  x{k |w |hm Io{k |Kw hm I{KC'ouuo'.|- w ZYZZ0ZYZZ?9\\\\\\\\\Zf ?9\ f '!6!!hh!r "hh!"!"ih"""!f 'BB%CB//3W 9L{rcwez}z9NK1zHGL\\ ''fU lPe(3'\| \fGe'V\V)\eY\Y5 )_\kh\~ b'=kf\e_\`k?+| ]k)F?'k, ::::, Yd4Z$ br> sb #K@;A;@KZ D Pj YTj Z D : Yv ` Pb Z PQ QU[D : YAt/{h5"" $$Y""kkPY"!$gvZ Z PP$ $$$Z Z n |i\R PKK!!KK\KKKK\KKr KK\KK KK_$'p9ppQcupQppup\  \R : $\$fE$~}$hW@zuiBY.BS |$$ X\NpwY #]^}"\S5b] pCH)VK4"e.Ko3~nH|383{-$U^'<.J/I ~i"W~W~JG$XoTNB((36SU^.^/.""h‚{=*m8Cet4N*N cy55q5 $!Ev ::::yl2 T 3FuEVlTb N*edca qcc{i@f#(: (K=FGeWD$, De%Mq*), f%H,]Mݼv ("s\\EVl(K%|\YwĶ``wOY/Qg Q'  [H88HH8wyzepbp^bTTzKp~r=T^{zwd*W **puto|ou\7 P\7 P\7 \|\x\3\IH!">$!"IIYvD xc Sg ĂM G Z Q j $j kk! 1OggO1\ihZZZZZZZZ|h [$k_k$_X o xQ tw{ue6||||otb [\ouuooud j d ]UtpvD xc \': T v\|w\| : P b \ZZs ZZmZZs YYwylpo4opip@wr'3\z z |&\pttp|}pu\7\7 o9]*T^'^Tc`$ ALx3}q-q}xqkp3+x'L Azqvuopl\9\ދvqsK|c]\\c{}t=u~_H0H/~^tt^}/G/G^}t=t}G/G0}=ut=}/Hq hQ eiiiz+Z%X~zsugTw'^T *^TP$bv~o ܚ yykj{'NT^wTgusz%XZii b'Cu90zíz|yo|w6 [''5Y  uxdtv1]\.xz'K52bC\  46 |zidd{x@/twj 48%L|}zzyx &' cv$2\Zh w'm|09xzddiËz{ 74 !wu'uud2'\\-wzzz||K&85\wC3'\! Y4''Z 6w|oy|zIJz09ucwv c5L&yx-\\12 ]T Z&&V]C :V$&Z]T ]&Y%%V]C Y&&V]AT"v3XST8P9bqwů{H_Pgfht{pgaěƱȎtȪ|Íg}9duEnjsJlrlj|MplwVjkcg{piUq~`tWxÓEhulu̓vz}zHZ[]_rcnyw{vtŠkƲ|wN`H%|& )TCS*wvjwvnܹ3\\\F F vR x||$ Cj P$d|G c 'ic $ $G $ P\>N BB$ Z"""| ""C$gkm| YCZ$$[ ?*[ C * TTTS1%pa fbkg`[dd%nmrrmmsrmmrrm@@a |ȫʐ3z{|::<=~~|}{}@EDJ)2cvtyz||}~~~ FΒŀeTLyD]]yyxvwuMG^ZEjygab.8\K"Ϟu[.d22IĈ` INO_B}~B_ k'Ǵ; D(-HIEDC9DIH#»mQ}`tǢĶĢt`r `SON[:aannvqlzhh!"h`tǢöžfimy,,hhr 3"33aaRsOOS`dHddT^M M28/TPPR\ĺĺ\RR\\Rb Nxkxwwxkxgg fhcddchfj hwwIlJI}I*{ C 3O0Iuc@ 5 (\-=haZ3o''nkjh''mG\"VӵB+4LEN3j737PxS63{E'=*}',TRRd)Nτ+=PPi=b NoE/pf'IGΣΔune. "pJ<"_>u/}-P e[͠ a-55>2mQKUlhe:'q,'KAfaxgs%߸F‰Y'^q8G@Lu;{Wb h/ \Zs p\u u` ('d]P\'(\g y S \'u {u` '\'{@('u@uN \y'{'i'{\vdFz$'\ }\F\\ PZLC\c_aN I HL\ aN cC0 $x5lLFrdmLKl\2 `0 PMx lLKm'\rLFlT^^TV0 $B MlKLmrFLlH 0 Px BlFLr\mKLl\H \yp\>$7 {2JS]biqu| 05jq{ "'+8<AEJOTar~ #'+059EKOS[fmry~  & ouuo\ou\\uo\( uo'j&$ [ *# ouY *  !0 & ) $ $ *6 8  9' \\Zs  \H~H V $.$- $- T^^s5 )ttp ou T^ J 1I ''? "k,P%  mmmmmm S \ wCl&$&$s&S%U ^S ''G < 0 2 pHHp{ ]= N .- '|\3 T VT^^s \  '' <+ |\P J \ Q3 z z :  h W E ou9 8   W $ * \ S S }oOPe Z mrrmmrn vq U Y4 W x .%D   @ D  o| J){ xV \ } P'P : w !!  Ch n]zUQ v pHHpg '  t \\ (J \  aF q$  q  5y } {J .- $$ \ \ P OZ \  | puto f \' j  v$ !!  ¸  cicccccc4c t+c:g!cnccb:-IZcb8c&c000077500000000000000000000000001262047200400367205ustar00rootroot00000000000000cookiecutter-1.3.0/tests/test-generate-binaries/input{{cookiecutter.binary_test}}/{{cookiecutter.binary_test}}.DS_Store000066400000000000000000000300041262047200400404000ustar00rootroot00000000000000cookiecutter-1.3.0/tests/test-generate-binaries/input{{cookiecutter.binary_test}}/{{cookiecutter.binary_test}}Bud1Storedi .DS_Storedilcblob >v1b some_font.otfdilcblob SO1b  @ @ @ @ EDSDB ` @ @ @ E DSDB ` @ @ @ some_font.otfdilcblob SO1btmpbwspblobbplist00  \WindowBounds[ShowSidebar]ShowStatusBar[ShowPathbar[ShowToolbar\SidebarWidth_{{653, 209}, {770, 438}}  ". VS`i̯ 8Rnkk22;A!.΁YϪV!}{G,Zy':jT:S@^p=BrlHL`(*RX ϺaL~O>P@ GG;M'<: A7z Oq/\?) c[ 'P l^Q^([&` ܥc[~ !Ӯ/ulJwʲOB2}Ixt]"m\8Rd^ E+fJXԡg5Jn5ql3` 5"K%pc[/]Q@`:0*w8z+1uW̑g?zO"F[q*(as;pppN6k>78 uN-pl+pp-)8T ] 3"vm L7юm%owFrbNul$zWW&(#z z|bdCR4 '>b/\Lj xuG nSu ,!^pűiplk0!9^wl; D_5o泌$9e; '8u p#тd8j2 QSaCj2?)dzpZѱ-AS3`헰~,tlkb>1 D :1`P5 c֥0br)Z= 8%dSQֱ oF TRbt ݿp=&T!!zD' MYRQGqy(;ݎm[Y%:􎒱_x$s=0? ^C+P O]_pl30mg.pk&0`2SԹ8Y|13ehBan#Zw5Щ5[Q~ mc`a_֫D0A3slKpLPԸ/&y9put=n'F:xҥGGVbND]?#Ev=L%8P,F*tkle 6`|z~ЭL8c[P(f?%=U n{c[ձhhKIK?2+(%u=6=Оvj~# o::wR:m+9}-kR9ސ~WHpMGX[D 3 x4iQ@pp=Dc+Z37Ю<\k}ޡ4>(vu.74*ݼ >5ӵ@ߡeaD?Mm[Vn {QVm eüdHِ^s8 "sn*QI SuMONjdc[=1{3@ݰ BqtlJ!r]%#*@3&z(f\izEk+mx?I#@L6¬~JxU P!r}_rte4(BBS>Sb,?pl"ʵ=fP\Ůo5E#۸SnmE?6}h:Lh2/3+NN1E$rlkdeh;ٗ_1zsڀ)E~}?zO":hޤOxHv>‰z҃e(7֘4E 3KZD9R38+ڿ?5ӞZ%sh 'Yx{uWOzen2$cLw'iA+JCI#ӈk0Nb2Ɯԛql#fNubɱ4!iYzgupտQ\VXyP2Hq:iPmoʅ0>TU%.qRǶn^C~*PA\m-#3=>wjR.Wu(}ǶBYEEefHs3㕡b4 HmMAyP#ˁn.ȪW~'iE΋}DؖzB{ +$J\#.(7S8u!݃c'O!αE?03A}0mB\|81c[VW>g:5/"'H/Ƕ*ZGǁ2उޓul+JU ߏ5slnG / RqJ pDEG4=Ci_BWP4˅xO^Pw`S|c[SDFrn(p̯?73 iIXR=D_8Uc:=72Y2N-& <6CcWN >;v=m0A6&Ѿ=wA1OԌe4 );h=a#S;f߯rRݸS)z3XsgCrc45׺oʮIIY1-wcZ ݠ]wGpC >%Lz $\v=e (E 26mYg8ՃAE?B{DX]ʸb,Nu=.S\Ͽ5 @}(w 5Ԑ+x (XuUpM/&M+}M yӅXI(QS,A'_hJ"ikcW#dI%GJfǥלṨ.|/cU$m}{;@=K҃v }2|/s8PzNxnx1ǶFfj@~0 ";:Rh9nj8 m!um3?NOr׼'!>?{5 -.=0Z>uI}ZűA(0uU7T:Tuo I؈݆UT21{OQF]C[e2L VҾ!#}BVcZ l8##VơZ |yALZ9+[fdZ oellgBEHLZkvglassmusicsearchuni2709heartstarstaremptyuserfilmthlargeththlistokremovezoominzoomoutoffsignalcogtrashhomefiletimeroaddownloadaltdownloaduploadinboxplaycirclerepeatrefreshlistaltlockflagheadphonesvolumeoffvolumedownvolumeupqrcodebarcodetagtagsbookbookmarkprintcamerafontbolditalictextheighttextwidthalignleftaligncenteralignrightalignjustifylistindentleftindentrightfacetimevideopictureuni270FmapmarkeradjusttinteditsharecheckmovestepbackwardfastbackwardbackwardplaypausestopforwardfastforwardstepforwardejectchevronleftchevronrightplussignminussignremovesignoksignquestionsigninfosignscreenshotremovecircleokcirclebancirclearrowleftarrowrightarrowuparrowdownsharealtresizefullresizesmallexclamationsigngiftleaffireeyeopeneyeclosewarningsignplanecalendarrandomcommentsmagnetchevronupchevrondownretweetshoppingcartfolderclosefolderopenresizeverticalresizehorizontalhddbullhornbellcertificatethumbsupthumbsdownhandrighthandlefthandtophanddowncirclearrowrightcirclearrowleftcirclearrowtopcirclearrowdownglobewrenchtasksfilterbriefcasefullscreendashboardpaperclipheartemptylinkphonepushpinEurousdgbpsortsortbyalphabetsortbyalphabetaltsortbyordersortbyorderaltsortbyattributessortbyattributesaltuncheckedexpandcollapsecollapsetopJan KovarikGLYPHICONS Halflings-RegularGLYPHICONS Halflings{&07@FMQZ_fmqv&+8=KX]aft} ).28CLQU_dhlpuy "&+048<@FJNR[^dinsw|+ ! = * ,\% [ ! ou)t - \- - f "k" . p % V \. uo 4  * ^ T> ^] A 4'k c '>G ( 1 BB e ; U X  % 3 +3 5M uo 1 k1 % PK < )uto E S|M  J) ((} ST pHHpi \\ Iv|E Jq / J J J P\ 0 ¸ |JE |/ N~ pHHp : `4` ouR % Y \ T^ ^T T^^T 5 ` d\ r' $  \\ |Z  fg d S \ ^T c$N $c \~ \ PP \' $ \  $ \ v\wR \D sm { |  T \ $Q \Z -'V Tg c  h 'Y'$' (J \$ h \ \ Z 9  '\'g  U } J t  ' \ o <  '\ -ouuo d \\ !! w  '  ` ;%;-QE 7SWq'mBo t  7 9 . l @OyFUa F{  :Pf|!Tqp*Gr n<O`^#Gk8jU? 0 !~!!!"#J###$/$%-&&'9''(),))*3*****+$+b++ wR : Q 'N$|:/FUK?2n"i#ssEU}>>KV2n"i#sse.( )6@l m: Y: ' : $$| IMO^B}~B^ = P$= YT vX"ggU yb N¸%N 9999 T%'$}: $pp: \z $gz L L $$g\P L :|:" |$" |$" 9 Pou\R \,P%$9 .bchh78pp hhhhhhhhe.\s t\6V{l @ ''dff.( )e ; X @l .$|[ [ *Pou$Y g*C 7^ ] ^ S7 :p91g! 1gpv\Pw$< | \: $ \ \i{||Kjlm:`jn{~+ %+;:, $,~zܵjie;nkk,epttqss %,ej~l{njaiifn܆n{k}ke+ottrrs $ *+ + * P\ xw'}~Y  }7'\¸T]p p p p v]\Z $_b PQ }}  7$$t [ ?*[ ?2$h \j  }w'xC: d6eC $$>tdwZ P~ ~$$~_C[ *[ \2**t ***4~ **Pm o[P\Po Y\g Y +)#)6 ' ]\f Q &&F6/^ ] SS> *+ & ]?v[ '$'*w[ B%Z%B*@P$$%%@)+ *> mY@ %%v@?]**^ ] /6Eph : : |t'XP\XP\XDP\r'v|\ChN 1g 11'c SM G \**Y w x''\$\\$|w \|h: }}Y} PPi}}Y} h@OLO 3[y!!DD!!D!!r !!  [$F1).k5UFU޳ NbN:͒ _<ˑ%('Y#/hct \\g ޳ FVE1*.k4UGdj i' ''$!'TZ\t !'\  EU i|t \''%"'J N @`j'P\~ U 'Z@''@'@dAB''r'J $\J h g 0wW\\'\z\|||||k|\||||\| h g ''H0D'E0k@_ v&'Q ~_ HccLcd˲ʋcdKddv&&Q }_ kPPVVYeudcKdc˳ˋcdKcdv*|||1n tpVOVP  \ UcV}\y' xP\ P }}~e,~e,}}h_h: \U|T^ J*\(**\*`0P\\PP\ƺƺ\Pw {: 5֤w|HIQ%i21hmasã#zYaa] F]| DC.(MDt{@RYjY2/dF[ЋWW0 *? gB!(;&##h & !K\;Q =Rp{5AJ2\'R'v \\\VQ R.iȋ\zN y -Sj;@;;@H\R.iɋ\zN y .R);@;;@i Q<+|<|+<+<i ;;I N~) $) |$|) $/I N~* ///I h*# \# \# \# @ *O '+3 J@]']@:7v@ O '% 3 @\@;7 D 1ibbiibbiU vxswwsswxr0Ju|OAAOOAAO~~iQ $--ymzy(/jj55jjjjon7o2a60[sxk&gsTbT]`(((([ *(   Wl#qdy&U!({NfXyx+dH0X0 o6-_2 NNl8u| 005Y>A %Ѷ뢉8\-5e㐋A FEba_g 8=| :#''BClZWU \| ZU Z\| U WCZw C$Dv~P{ {D| w P{| n \xPkPn ouY  x{k |w |hm Io{k |Kw hm I{KC'ouuo'.|- w ZYZZ0ZYZZ?9\\\\\\\\\Zf ?9\ f '!6!!hh!r "hh!"!"ih"""!f 'BB%CB//3W 9L{rcwez}z9NK1zHGL\\ ''fU lPe(3'\| \fGe'V\V)\eY\Y5 )_\kh\~ b'=kf\e_\`k?+| ]k)F?'k, ::::, Yd4Z$ br> sb #K@;A;@KZ D Pj YTj Z D : Yv ` Pb Z PQ QU[D : YAt/{h5"" $$Y""kkPY"!$gvZ Z PP$ $$$Z Z n |i\R PKK!!KK\KKKK\KKr KK\KK KK_$'p9ppQcupQppup\  \R : $\$fE$~}$hW@zuiBY.BS |$$ X\NpwY #]^}"\S5b] pCH)VK4"e.Ko3~nH|383{-$U^'<.J/I ~i"W~W~JG$XoTNB((36SU^.^/.""h‚{=*m8Cet4N*N cy55q5 $!Ev ::::yl2 T 3FuEVlTb N*edca qcc{i@f#(: (K=FGeWD$, De%Mq*), f%H,]Mݼv ("s\\EVl(K%|\YwĶ``wOY/Qg Q'  [H88HH8wyzepbp^bTTzKp~r=T^{zwd*W **puto|ou\7 P\7 P\7 \|\x\3\IH!">$!"IIYvD xc Sg ĂM G Z Q j $j kk! 1OggO1\ihZZZZZZZZ|h [$k_k$_X o xQ tw{ue6||||otb [\ouuooud j d ]UtpvD xc \': T v\|w\| : P b \ZZs ZZmZZs YYwylpo4opip@wr'3\z z |&\pttp|}pu\7\7 o9]*T^'^Tc`$ ALx3}q-q}xqkp3+x'L Azqvuopl\9\ދvqsK|c]\\c{}t=u~_H0H/~^tt^}/G/G^}t=t}G/G0}=ut=}/Hq hQ eiiiz+Z%X~zsugTw'^T *^TP$bv~o ܚ yykj{'NT^wTgusz%XZii b'Cu90zíz|yo|w6 [''5Y  uxdtv1]\.xz'K52bC\  46 |zidd{x@/twj 48%L|}zzyx &' cv$2\Zh w'm|09xzddiËz{ 74 !wu'uud2'\\-wzzz||K&85\wC3'\! Y4''Z 6w|oy|zIJz09ucwv c5L&yx-\\12 ]T Z&&V]C :V$&Z]T ]&Y%%V]C Y&&V]AT"v3XST8P9bqwů{H_Pgfht{pgaěƱȎtȪ|Íg}9duEnjsJlrlj|MplwVjkcg{piUq~`tWxÓEhulu̓vz}zHZ[]_rcnyw{vtŠkƲ|wN`H%|& )TCS*wvjwvnܹ3\\\F F vR x||$ Cj P$d|G c 'ic $ $G $ P\>N BB$ Z"""| ""C$gkm| YCZ$$[ ?*[ C * TTTS1%pa fbkg`[dd%nmrrmmsrmmrrm@@a |ȫʐ3z{|::<=~~|}{}@EDJ)2cvtyz||}~~~ FΒŀeTLyD]]yyxvwuMG^ZEjygab.8\K"Ϟu[.d22IĈ` INO_B}~B_ k'Ǵ; D(-HIEDC9DIH#»mQ}`tǢĶĢt`r `SON[:aannvqlzhh!"h`tǢöžfimy,,hhr 3"33aaRsOOS`dHddT^M M28/TPPR\ĺĺ\RR\\Rb Nxkxwwxkxgg fhcddchfj hwwIlJI}I*{ C 3O0Iuc@ 5 (\-=haZ3o''nkjh''mG\"VӵB+4LEN3j737PxS63{E'=*}',TRRd)Nτ+=PPi=b NoE/pf'IGΣΔune. "pJ<"_>u/}-P e[͠ a-55>2mQKUlhe:'q,'KAfaxgs%߸F‰Y'^q8G@Lu;{Wb h/ \Zs p\u u` ('d]P\'(\g y S \'u {u` '\'{@('u@uN \y'{'i'{\vdFz$'\ }\F\\ PZLC\c_aN I HL\ aN cC0 $x5lLFrdmLKl\2 `0 PMx lLKm'\rLFlT^^TV0 $B MlKLmrFLlH 0 Px BlFLr\mKLl\H \yp\>$7 {2JS]biqu| 05jq{ "'+8<AEJOTar~ #'+059EKOS[fmry~  & ouuo\ou\\uo\( uo'j&$ [ *# ouY *  !0 & ) $ $ *6 8  9' \\Zs  \H~H V $.$- $- T^^s5 )ttp ou T^ J 1I ''? "k,P%  mmmmmm S \ wCl&$&$s&S%U ^S ''G < 0 2 pHHp{ ]= N .- '|\3 T VT^^s \  '' <+ |\P J \ Q3 z z :  h W E ou9 8   W $ * \ S S }oOPe Z mrrmmrn vq U Y4 W x .%D   @ D  o| J){ xV \ } P'P : w !!  Ch n]zUQ v pHHpg '  t \\ (J \  aF q$  q  5y } {J .- $$ \ \ P OZ \  | puto f \' j  v$ !!  ¸  cicccccc4c t+c:g!cnccb:-IZcb8c&c000077500000000000000000000000001262047200400447225ustar00rootroot00000000000000cookiecutter-1.3.0/tests/test-generate-binaries/input{{cookiecutter.binary_test}}/{{cookiecutter.binary_test}}/{{cookiecutter.binary_test}}logo.png000066400000000000000000000104351262047200400463730ustar00rootroot00000000000000cookiecutter-1.3.0/tests/test-generate-binaries/input{{cookiecutter.binary_test}}/{{cookiecutter.binary_test}}/{{cookiecutter.binary_test}}PNG  IHDR.NsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATx{ŝ3E hTh!.5Bha@Q;F=50' 㮯MVVeXQh #"SUGzzv߹;{nj!-,]zp)&)Ov`5ey7tЌ D1»-יsFRV7sr9#8R oU( J8 H4qri)WH,ճHӁARf6sgvGA]йlˇced]+<&43G?mˡc*ɔPsFF(o=!r<-u غлe%n\ m4gdS"Dr7oADY VS`i̯ 8Rnkk22;A!.΁YϪV!}{G,Zy':jT:S@^p=BrlHL`(*RX ϺaL~O>P@ GG;M'<: A7z Oq/\?) c[ 'P l^Q^([&` ܥc[~ !Ӯ/ulJwʲOB2}Ixt]"m\8Rd^ E+fJXԡg5Jn5ql3` 5"K%pc[/]Q@`:0*w8z+1uW̑g?zO"F[q*(as;pppN6k>78 uN-pl+pp-)8T ] 3"vm L7юm%owFrbNul$zWW&(#z z|bdCR4 '>b/\Lj xuG nSu ,!^pűiplk0!9^wl; D_5o泌$9e; '8u p#тd8j2 QSaCj2?)dzpZѱ-AS3`헰~,tlkb>1 D :1`P5 c֥0br)Z= 8%dSQֱ oF TRbt ݿp=&T!!zD' MYRQGqy(;ݎm[Y%:􎒱_x$s=0? ^C+P O]_pl30mg.pk&0`2SԹ8Y|13ehBan#Zw5Щ5[Q~ mc`a_֫D0A3slKpLPԸ/&y9put=n'F:xҥGGVbND]?#Ev=L%8P,F*tkle 6`|z~ЭL8c[P(f?%=U n{c[ձhhKIK?2+(%u=6=Оvj~# o::wR:m+9}-kR9ސ~WHpMGX[D 3 x4iQ@pp=Dc+Z37Ю<\k}ޡ4>(vu.74*ݼ >5ӵ@ߡeaD?Mm[Vn {QVm eüdHِ^s8 "sn*QI SuMONjdc[=1{3@ݰ BqtlJ!r]%#*@3&z(f\izEk+mx?I#@L6¬~JxU P!r}_rte4(BBS>Sb,?pl"ʵ=fP\Ůo5E#۸SnmE?6}h:Lh2/3+NN1E$rlkdeh;ٗ_1zsڀ)E~}?zO":hޤOxHv>‰z҃e(7֘4E 3KZD9R38+ڿ?5ӞZ%sh 'Yx{uWOzen2$cLw'iA+JCI#ӈk0Nb2Ɯԛql#fNubɱ4!iYzgupտQ\VXyP2Hq:iPmoʅ0>TU%.qRǶn^C~*PA\m-#3=>wjR.Wu(}ǶBYEEefHs3㕡b4 HmMAyP#ˁn.ȪW~'iE΋}DؖzB{ +$J\#.(7S8u!݃c'O!αE?03A}0mB\|81c[VW>g:5/"'H/Ƕ*ZGǁ2उޓul+JU ߏ5slnG / RqJ pDEG4=Ci_BWP4˅xO^Pw`S|c[SDFrn(p̯?73 iIXR=D_8Uc:=72Y2N-& <6CcWN >;v=m0A6&Ѿ=wA1OԌe4 );h=a#S;f߯rRݸS)z3XsgCrc45׺oʮIIY1-wcZ ݠ]wGpC >%Lz $\v=e (E 26mYg8ՃAE?B{DX]ʸb,Nu=.S\Ͽ5 @}(w 5Ԑ+x (XuUpM/&M+}M yӅXI(QS,A'_hJ"ikcW#dI%GJfǥלṨ.|/cU$m}{;@=K҃v }2|/s8PzNxnx1ǶFfj@~0 ";:Rh9nj8 m!um3?NOr׼'!>?{5 -.=0Z>uI}ZűA(0uU7T:Tuo I؈݆UT21{OQF]C[e2L VҾ!#}BVcZ l8##VơZ |yALZ9+[fdZ oellg'}} ) assert input_dir == '' def test_abbreviation_expansion_builtin(): input_dir = main.expand_abbreviations( 'gh:a', {} ) assert input_dir == 'https://github.com/a.git' def test_abbreviation_expansion_override_builtin(): input_dir = main.expand_abbreviations( 'gh:a', {'abbreviations': {'gh': '<{0}>'}} ) assert input_dir == '' def test_abbreviation_expansion_prefix_ignores_suffix(): input_dir = main.expand_abbreviations( 'xx:a', {'abbreviations': {'xx': '<>'}} ) assert input_dir == '<>' def test_abbreviation_expansion_prefix_not_0_in_braces(): with pytest.raises(IndexError): main.expand_abbreviations('xx:a', {'abbreviations': {'xx': '{1}'}}) cookiecutter-1.3.0/tests/test_abort_generate_on_hook_error.py000066400000000000000000000021171262047200400246720ustar00rootroot00000000000000# -*- coding: utf-8 -*- import pytest from cookiecutter import generate from cookiecutter import exceptions @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() @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() cookiecutter-1.3.0/tests/test_cli.py000066400000000000000000000163611262047200400175410ustar00rootroot00000000000000import os import pytest from click.testing import CliRunner from cookiecutter.cli import main from cookiecutter.main import cookiecutter from cookiecutter import utils, config runner = CliRunner() @pytest.fixture def remove_fake_project_dir(request): """ Remove the fake project directory created during the tests. """ def fin_remove_fake_project_dir(): if os.path.isdir('fake-project'): utils.rmtree('fake-project') request.addfinalizer(fin_remove_fake_project_dir) @pytest.fixture def make_fake_project_dir(request): """Create a fake project to be overwritten in the according tests.""" os.makedirs('fake-project') @pytest.fixture(params=['-V', '--version']) def version_cli_flag(request): return request.param def test_cli_version(version_cli_flag): result = runner.invoke(main, [version_cli_flag]) assert result.exit_code == 0 assert result.output.startswith('Cookiecutter') @pytest.mark.usefixtures('make_fake_project_dir', 'remove_fake_project_dir') def test_cli_error_on_existing_output_directory(): result = runner.invoke(main, ['tests/fake-repo-pre/', '--no-input']) assert result.exit_code != 0 expected_error_msg = 'Error: "fake-project" directory already exists\n' assert result.output == expected_error_msg @pytest.mark.usefixtures('remove_fake_project_dir') def test_cli(): result = runner.invoke(main, ['tests/fake-repo-pre/', '--no-input']) assert result.exit_code == 0 assert os.path.isdir('fake-project') @pytest.mark.usefixtures('remove_fake_project_dir') def test_cli_verbose(): result = runner.invoke(main, ['tests/fake-repo-pre/', '--no-input', '-v']) assert result.exit_code == 0 assert os.path.isdir('fake-project') @pytest.mark.usefixtures('remove_fake_project_dir') def test_cli_replay(mocker): mock_cookiecutter = mocker.patch( 'cookiecutter.cli.cookiecutter' ) template_path = 'tests/fake-repo-pre/' result = runner.invoke(main, [ template_path, '--replay', '-v' ]) assert result.exit_code == 0 mock_cookiecutter.assert_called_once_with( template_path, None, False, replay=True, overwrite_if_exists=False, output_dir='.', config_file=config.USER_CONFIG_PATH ) @pytest.mark.usefixtures('remove_fake_project_dir') def test_cli_exit_on_noinput_and_replay(mocker): mock_cookiecutter = mocker.patch( 'cookiecutter.cli.cookiecutter', side_effect=cookiecutter ) template_path = 'tests/fake-repo-pre/' result = runner.invoke(main, [ template_path, '--no-input', '--replay', '-v' ]) assert result.exit_code == 1 expected_error_msg = ( "You can not use both replay and no_input or extra_context " "at the same time." ) assert expected_error_msg in result.output mock_cookiecutter.assert_called_once_with( template_path, None, True, replay=True, overwrite_if_exists=False, output_dir='.', config_file=config.USER_CONFIG_PATH ) @pytest.fixture(params=['-f', '--overwrite-if-exists']) def overwrite_cli_flag(request): return request.param @pytest.mark.usefixtures('remove_fake_project_dir') def test_run_cookiecutter_on_overwrite_if_exists_and_replay( mocker, overwrite_cli_flag): mock_cookiecutter = mocker.patch( 'cookiecutter.cli.cookiecutter', side_effect=cookiecutter ) template_path = 'tests/fake-repo-pre/' result = runner.invoke(main, [ template_path, '--replay', '-v', overwrite_cli_flag, ]) assert result.exit_code == 0 mock_cookiecutter.assert_called_once_with( template_path, None, False, replay=True, overwrite_if_exists=True, output_dir='.', config_file=config.USER_CONFIG_PATH ) @pytest.mark.usefixtures('remove_fake_project_dir') def test_cli_overwrite_if_exists_when_output_dir_does_not_exist( overwrite_cli_flag): result = runner.invoke(main, [ 'tests/fake-repo-pre/', '--no-input', overwrite_cli_flag ]) assert result.exit_code == 0 assert os.path.isdir('fake-project') @pytest.mark.usefixtures('make_fake_project_dir', 'remove_fake_project_dir') def test_cli_overwrite_if_exists_when_output_dir_exists(overwrite_cli_flag): result = runner.invoke(main, [ 'tests/fake-repo-pre/', '--no-input', overwrite_cli_flag ]) assert result.exit_code == 0 assert os.path.isdir('fake-project') @pytest.fixture(params=['-o', '--output-dir']) def output_dir_flag(request): return request.param @pytest.fixture def output_dir(tmpdir): return str(tmpdir.mkdir('output')) def test_cli_output_dir(mocker, output_dir_flag, output_dir): mock_cookiecutter = mocker.patch( 'cookiecutter.cli.cookiecutter' ) template_path = 'tests/fake-repo-pre/' result = runner.invoke(main, [ template_path, output_dir_flag, output_dir ]) assert result.exit_code == 0 mock_cookiecutter.assert_called_once_with( template_path, None, False, replay=False, overwrite_if_exists=False, output_dir=output_dir, config_file=config.USER_CONFIG_PATH ) @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') @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='.', config_file=None ) cookiecutter-1.3.0/tests/test_cookiecutter_invocation.py000066400000000000000000000024001262047200400237100ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test_cookiecutter_invocation ---------------------------- Tests to make sure that cookiecutter can be called from the cli without using the entry point set up for the package. """ import os import pytest import subprocess import sys from cookiecutter import utils def test_should_raise_error_without_template_arg(capfd): with pytest.raises(subprocess.CalledProcessError): subprocess.check_call(['python', '-m', 'cookiecutter.cli']) _, err = capfd.readouterr() exp_message = 'Error: Missing argument "template".' assert exp_message in err @pytest.fixture def project_dir(request): """Remove the rendered project directory created by the test.""" rendered_dir = 'fake-project-templated' def remove_generated_project(): if os.path.isdir(rendered_dir): utils.rmtree(rendered_dir) request.addfinalizer(remove_generated_project) return rendered_dir @pytest.mark.usefixtures('clean_system') def test_should_invoke_main(monkeypatch, project_dir): monkeypatch.setenv('PYTHONPATH', '.') subprocess.check_call([ sys.executable, '-m', 'cookiecutter.cli', 'tests/fake-repo-tmpl', '--no-input' ]) assert os.path.isdir(project_dir) cookiecutter-1.3.0/tests/test_cookiecutter_local_no_input.py000066400000000000000000000051601262047200400245520ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_cookiecutter_local_no_input -------------------------------- Tests formerly known from a unittest residing in test_main.py named TestCookiecutterLocalNoInput.test_cookiecutter TestCookiecutterLocalNoInput.test_cookiecutter_no_slash TestCookiecutterLocalNoInput.test_cookiecutter_no_input_extra_context TestCookiecutterLocalNoInput.test_cookiecutter_templated_context """ import os import pytest from cookiecutter import main, utils @pytest.fixture(scope='function') def remove_additional_dirs(request): """ Remove special directories which are created during the tests. """ def fin_remove_additional_dirs(): if os.path.isdir('fake-project'): utils.rmtree('fake-project') if os.path.isdir('fake-project-extra'): utils.rmtree('fake-project-extra') if os.path.isdir('fake-project-templated'): utils.rmtree('fake-project-templated') request.addfinalizer(fin_remove_additional_dirs) @pytest.fixture(params=['tests/fake-repo-pre/', 'tests/fake-repo-pre']) def bake(request): """ Run cookiecutter with the given input_dir path. """ main.cookiecutter(request.param, no_input=True) @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs', 'bake') def test_cookiecutter(): assert os.path.isdir('tests/fake-repo-pre/{{cookiecutter.repo_name}}') assert not os.path.isdir('tests/fake-repo-pre/fake-project') assert os.path.isdir('fake-project') assert os.path.isfile('fake-project/README.rst') assert not os.path.exists('fake-project/json/') @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') def test_cookiecutter_no_input_extra_context(): """ `Call cookiecutter()` with `no_input=True` and `extra_context """ main.cookiecutter( 'tests/fake-repo-pre', no_input=True, extra_context={'repo_name': 'fake-project-extra'} ) assert os.path.isdir('fake-project-extra') @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') def test_cookiecutter_templated_context(): """ `Call cookiecutter()` with `no_input=True` and templates in the cookiecutter.json file """ main.cookiecutter( 'tests/fake-repo-tmpl', no_input=True ) assert os.path.isdir('fake-project-templated') @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') def test_cookiecutter_no_input_return_project_dir(): """Call `cookiecutter()` with `no_input=True`.""" project_dir = main.cookiecutter('tests/fake-repo-pre', no_input=True) assert project_dir == os.path.abspath('fake-project') cookiecutter-1.3.0/tests/test_cookiecutter_local_with_input.py000066400000000000000000000036311262047200400251120ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_cookiecutter_local_with_input ---------------------------------- Tests formerly known from a unittest residing in test_main.py named TestCookiecutterLocalWithInput.test_cookiecutter_local_with_input TestCookiecutterLocalWithInput.test_cookiecutter_input_extra_context """ import os import pytest from cookiecutter import main, utils @pytest.fixture(scope='function') def remove_additional_dirs(request): """ Remove special directories which are created during the tests. """ def fin_remove_additional_dirs(): if os.path.isdir('fake-project'): utils.rmtree('fake-project') if os.path.isdir('fake-project-input-extra'): utils.rmtree('fake-project-input-extra') request.addfinalizer(fin_remove_additional_dirs) @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') def test_cookiecutter_local_with_input(monkeypatch): monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: default ) main.cookiecutter('tests/fake-repo-pre/', no_input=False) assert os.path.isdir('tests/fake-repo-pre/{{cookiecutter.repo_name}}') assert not os.path.isdir('tests/fake-repo-pre/fake-project') assert os.path.isdir('fake-project') assert os.path.isfile('fake-project/README.rst') assert not os.path.exists('fake-project/json/') @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') def test_cookiecutter_input_extra_context(monkeypatch): """ `Call cookiecutter()` with `no_input=False` and `extra_context` """ monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: default ) main.cookiecutter( 'tests/fake-repo-pre', no_input=False, extra_context={'repo_name': 'fake-project-input-extra'} ) assert os.path.isdir('fake-project-input-extra') cookiecutter-1.3.0/tests/test_cookiecutter_repo_arg.py000066400000000000000000000043171262047200400233460ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_cookiecutter_repo_arg -------------------------- Tests formerly known from a unittest residing in test_main.py named TestCookiecutterRepoArg.test_cookiecutter_git TestCookiecutterRepoArg.test_cookiecutter_mercurial """ from __future__ import unicode_literals import os import pytest from cookiecutter import main, utils from tests.skipif_markers import skipif_no_network @pytest.fixture(scope='function') def remove_additional_folders(request): """ Remove some special folders which are created by the tests. """ def fin_remove_additional_folders(): if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('python_boilerplate'): utils.rmtree('python_boilerplate') if os.path.isdir('cookiecutter-trytonmodule'): utils.rmtree('cookiecutter-trytonmodule') if os.path.isdir('module_name'): utils.rmtree('module_name') request.addfinalizer(fin_remove_additional_folders) @skipif_no_network @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_cookiecutter_git(): main.cookiecutter( 'https://github.com/audreyr/cookiecutter-pypackage.git', no_input=True ) clone_dir = os.path.join( os.path.expanduser('~/.cookiecutters'), 'cookiecutter-pypackage' ) assert os.path.exists(clone_dir) assert os.path.isdir('python_boilerplate') assert os.path.isfile('python_boilerplate/README.rst') assert os.path.exists('python_boilerplate/setup.py') @skipif_no_network @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_cookiecutter_mercurial(monkeypatch): monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: default ) main.cookiecutter('https://bitbucket.org/pokoli/cookiecutter-trytonmodule') clone_dir = os.path.join( os.path.expanduser('~/.cookiecutters'), 'cookiecutter-trytonmodule' ) assert os.path.exists(clone_dir) assert os.path.isdir('module_name') assert os.path.isfile('module_name/README') assert os.path.exists('module_name/setup.py') cookiecutter-1.3.0/tests/test_cookiecutters.py000066400000000000000000000040141262047200400216450ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_cookiecutters ------------------ Tests formerly known from a unittest residing in test_examples.py named TestPyPackage.test_cookiecutter_pypackage TestJQuery.test_cookiecutter_jquery """ from __future__ import unicode_literals import os import sys import subprocess import pytest from cookiecutter import utils from tests.skipif_markers import skipif_travis, skipif_no_network @pytest.fixture(scope='function') def remove_additional_dirs(request): """ Remove special directories which are creating during the tests. """ def fin_remove_additional_dirs(): for path in ('cookiecutter-pypackage', 'cookiecutter-jquery', 'python_boilerplate', 'boilerplate'): if os.path.isdir(path): utils.rmtree(path) request.addfinalizer(fin_remove_additional_dirs) def bake_data(): pypackage_data = ( 'git clone https://github.com/audreyr/cookiecutter-pypackage.git', '{0} -m cookiecutter.cli --no-input cookiecutter-pypackage/'.format( sys.executable), 'cookiecutter-pypackage', 'python_boilerplate/README.rst' ) jquery_data = ( 'git clone https://github.com/audreyr/cookiecutter-jquery.git', '{0} -m cookiecutter.cli --no-input cookiecutter-jquery/'.format( sys.executable), 'cookiecutter-jquery', 'boilerplate/README.md' ) yield pypackage_data yield jquery_data @skipif_travis @skipif_no_network @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') @pytest.mark.parametrize('git_cmd, bake_cmd, out_dir, readme', bake_data()) def test_cookiecutters(git_cmd, bake_cmd, out_dir, readme): """ Tests that the given cookiecutters work as expected. """ proc = subprocess.Popen(git_cmd, stdin=subprocess.PIPE, shell=True) proc.wait() proc = subprocess.Popen(bake_cmd, stdin=subprocess.PIPE, shell=True) proc.wait() assert os.path.isdir(out_dir) assert os.path.isfile(readme) cookiecutter-1.3.0/tests/test_find.py000077500000000000000000000007551262047200400177150ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_find --------- Tests for `cookiecutter.find` module. """ import os import pytest from cookiecutter import find @pytest.fixture(params=['fake-repo-pre', 'fake-repo-pre2']) def repo_dir(request): return os.path.join('tests', request.param) def test_find_template(repo_dir): template = find.find_template(repo_dir=repo_dir) test_dir = os.path.join(repo_dir, '{{cookiecutter.repo_name}}') assert template == test_dir cookiecutter-1.3.0/tests/test_generate_context.py000066400000000000000000000124641262047200400223300ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_generate_context --------------------- Tests formerly known from a unittest residing in test_generate.py named TestGenerateContext.test_generate_context TestGenerateContext.test_generate_context_with_default TestGenerateContext.test_generate_context_with_extra TestGenerateContext.test_generate_context_with_default_and_extra """ from __future__ import unicode_literals import pytest import os import re from collections import OrderedDict from cookiecutter import generate from cookiecutter.exceptions import ContextDecodingException def context_data(): context = ( { 'context_file': 'tests/test-generate-context/test.json' }, { 'test': {'1': 2, 'some_key': 'some_val'} } ) context_with_default = ( { 'context_file': 'tests/test-generate-context/test.json', 'default_context': {'1': 3} }, { 'test': {'1': 3, 'some_key': 'some_val'} } ) context_with_extra = ( { 'context_file': 'tests/test-generate-context/test.json', 'extra_context': {'1': 4}, }, { 'test': {'1': 4, 'some_key': 'some_val'} } ) context_with_default_and_extra = ( { 'context_file': 'tests/test-generate-context/test.json', 'default_context': {'1': 3}, 'extra_context': {'1': 5}, }, { 'test': {'1': 5, 'some_key': 'some_val'} } ) yield context yield context_with_default yield context_with_extra yield context_with_default_and_extra @pytest.mark.usefixtures('clean_system') @pytest.mark.parametrize('input_params, expected_context', context_data()) def test_generate_context(input_params, expected_context): """ Test the generated context for several input parameters against the according expected context. """ assert generate.generate_context(**input_params) == expected_context @pytest.mark.usefixtures('clean_system') def test_generate_context_with_json_decoding_error(): with pytest.raises(ContextDecodingException) as excinfo: generate.generate_context( 'tests/test-generate-context/invalid-syntax.json' ) # original message from json module should be included pattern = ( 'Expecting \'{0,1}:\'{0,1} delimiter: ' 'line 1 column (19|20) \(char 19\)' ) assert re.search(pattern, str(excinfo.value)) # File name should be included too...for testing purposes, just test the # last part of the file. If we wanted to test the absolute path, we'd have # to do some additional work in the test which doesn't seem that needed at # this point. path = os.path.sep.join( ['tests', 'test-generate-context', 'invalid-syntax.json'] ) assert path in str(excinfo.value) @pytest.fixture def default_context(): return { 'not_in_template': 'foobar', 'project_name': 'Kivy Project', 'orientation': 'landscape' } @pytest.fixture def extra_context(): return { 'also_not_in_template': 'foobar2', 'github_username': 'hackebrot', } @pytest.fixture def context_file(): return 'tests/test-generate-context/choices_template.json' def test_choices(context_file, default_context, extra_context): """Make sure that the default for list variables is based on the user config and the list as such is not changed to a single value. """ expected_context = { 'choices_template': OrderedDict([ ('full_name', 'Raphael Pierzina'), ('github_username', 'hackebrot'), ('project_name', 'Kivy Project'), ('repo_name', '{{cookiecutter.project_name|lower}}'), ('orientation', ['landscape', 'all', 'portrait']), ]) } generated_context = generate.generate_context( context_file, default_context, extra_context ) assert generated_context == expected_context @pytest.fixture def template_context(): return OrderedDict([ ('full_name', 'Raphael Pierzina'), ('github_username', 'hackebrot'), ('project_name', 'Kivy Project'), ('repo_name', '{{cookiecutter.project_name|lower}}'), ('orientation', ['all', 'landscape', 'portrait']), ]) def test_apply_overwrites_does_include_unused_variables(template_context): generate.apply_overwrites_to_context( template_context, {'not in template': 'foobar'} ) assert 'not in template' not in template_context def test_apply_overwrites_sets_non_list_value(template_context): generate.apply_overwrites_to_context( template_context, {'repo_name': 'foobar'} ) assert template_context['repo_name'] == 'foobar' def test_apply_overwrites_does_not_modify_choices_for_invalid_overwrite( template_context): generate.apply_overwrites_to_context( template_context, {'orientation': 'foobar'} ) assert template_context['orientation'] == ['all', 'landscape', 'portrait'] def test_apply_overwrites_sets_default_for_choice_variable(template_context): generate.apply_overwrites_to_context( template_context, {'orientation': 'landscape'} ) assert template_context['orientation'] == ['landscape', 'all', 'portrait'] cookiecutter-1.3.0/tests/test_generate_copy_without_render.py000066400000000000000000000042761262047200400247420ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_generate_copy_without_render --------------------------------- """ from __future__ import unicode_literals import os import pytest from cookiecutter import generate from cookiecutter import utils @pytest.fixture(scope='function') def remove_test_dir(request): """ Remove the folder that is created by the test. """ def fin_remove_test_dir(): if os.path.exists('test_copy_without_render'): utils.rmtree('test_copy_without_render') request.addfinalizer(fin_remove_test_dir) @pytest.mark.usefixtures('clean_system', 'remove_test_dir') def test_generate_copy_without_render_extensions(): generate.generate_files( context={ 'cookiecutter': { 'repo_name': 'test_copy_without_render', 'render_test': 'I have been rendered!', '_copy_without_render': [ '*not-rendered', 'rendered/not_rendered.yml', '*.txt', ]} }, repo_dir='tests/test-generate-copy-without-render' ) dir_contents = os.listdir('test_copy_without_render') assert '{{cookiecutter.repo_name}}-not-rendered' in dir_contents assert 'test_copy_without_render-rendered' in dir_contents with open('test_copy_without_render/README.txt') as f: assert '{{cookiecutter.render_test}}' in f.read() with open('test_copy_without_render/README.rst') as f: assert 'I have been rendered!' in f.read() with open('test_copy_without_render/' 'test_copy_without_render-rendered/' 'README.txt') as f: assert '{{cookiecutter.render_test}}' in f.read() with open('test_copy_without_render/' 'test_copy_without_render-rendered/' 'README.rst') as f: assert 'I have been rendered' in f.read() with open('test_copy_without_render/' '{{cookiecutter.repo_name}}-not-rendered/' 'README.rst') as f: assert '{{cookiecutter.render_test}}' in f.read() with open('test_copy_without_render/rendered/not_rendered.yml') as f: assert '{{cookiecutter.render_test}}' in f.read() cookiecutter-1.3.0/tests/test_generate_file.py000077500000000000000000000061401262047200400215600ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_generate_file ------------------ Tests formerly known from a unittest residing in test_generate.py named TestGenerateFile.test_generate_file TestGenerateFile.test_generate_file_verbose_template_syntax_error """ from __future__ import unicode_literals import os import pytest from jinja2 import FileSystemLoader from jinja2.environment import Environment from jinja2.exceptions import TemplateSyntaxError from cookiecutter import generate @pytest.fixture(scope='function') def remove_cheese_file(request): """ Remove the cheese text file which is created by the tests. """ def fin_remove_cheese_file(): if os.path.exists('tests/files/cheese.txt'): os.remove('tests/files/cheese.txt') request.addfinalizer(fin_remove_cheese_file) @pytest.fixture def env(): environment = Environment() environment.loader = FileSystemLoader('.') return environment @pytest.mark.usefixtures('remove_cheese_file') def test_generate_file(env): infile = 'tests/files/{{generate_file}}.txt' generate.generate_file( project_dir=".", infile=infile, context={'generate_file': 'cheese'}, env=env ) assert os.path.isfile('tests/files/cheese.txt') with open('tests/files/cheese.txt', 'rt') as f: generated_text = f.read() assert generated_text == 'Testing cheese' @pytest.mark.usefixtures('remove_cheese_file') def test_generate_file_with_false_condition(env): infile = 'tests/files/{% if generate_file == \'y\' %}cheese.txt{% endif %}' generate.generate_file( project_dir=".", infile=infile, context={'generate_file': 'n'}, env=env ) assert not os.path.exists('tests/files/cheese.txt') @pytest.mark.usefixtures('remove_cheese_file') def test_generate_file_with_true_conditional(env): infile = 'tests/files/{% if generate_file == \'y\' %}cheese.txt{% endif %}' generate.generate_file( project_dir=".", infile=infile, context={'generate_file': 'y'}, env=env ) assert os.path.isfile('tests/files/cheese.txt') with open('tests/files/cheese.txt', 'rt') as f: generated_text = f.read() assert generated_text == 'Testing that generate_file was y' @pytest.fixture def expected_msg(): msg = ( 'Missing end of comment tag\n' ' File "./tests/files/syntax_error.txt", line 1\n' ' I eat {{ syntax_error }} {# this comment is not closed}' ) return msg.replace("/", os.sep) @pytest.mark.usefixtures('remove_cheese_file') def test_generate_file_verbose_template_syntax_error(env, expected_msg): try: generate.generate_file( project_dir=".", infile='tests/files/syntax_error.txt', context={'syntax_error': 'syntax_error'}, env=env ) except TemplateSyntaxError as exception: assert str(exception) == expected_msg except Exception as exception: pytest.fail('Unexpected exception thrown: {0}'.format(exception)) else: pytest.fail('TemplateSyntaxError not thrown') cookiecutter-1.3.0/tests/test_generate_files.py000066400000000000000000000155041262047200400217440ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_generate_files ------------------- Tests formerly known from a unittest residing in test_generate.py named TestGenerateFiles.test_generate_files_nontemplated_exception TestGenerateFiles.test_generate_files TestGenerateFiles.test_generate_files_with_trailing_newline TestGenerateFiles.test_generate_files_binaries TestGenerateFiles.test_generate_files_absolute_path TestGenerateFiles.test_generate_files_output_dir TestGenerateFiles.test_generate_files_permissions Use the global clean_system fixture and run additional teardown code to remove some special folders. For a better understanding - order of fixture calls: clean_system setup code remove_additional_folders setup code remove_additional_folders teardown code clean_system teardown code """ from __future__ import unicode_literals import os import io import pytest from cookiecutter import generate from cookiecutter import exceptions from cookiecutter import utils @pytest.mark.parametrize('invalid_dirname', ['', '{foo}', '{{foo', 'bar}}']) def test_ensure_dir_is_templated_raises(invalid_dirname): with pytest.raises(exceptions.NonTemplatedInputDirException): generate.ensure_dir_is_templated(invalid_dirname) @pytest.fixture(scope='function') def remove_additional_folders(request): """ Remove some special folders which are created by the tests. """ def fin_remove_additional_folders(): if os.path.exists('inputpizzä'): utils.rmtree('inputpizzä') if os.path.exists('inputgreen'): utils.rmtree('inputgreen') if os.path.exists('inputbinary_files'): utils.rmtree('inputbinary_files') if os.path.exists('tests/custom_output_dir'): utils.rmtree('tests/custom_output_dir') if os.path.exists('inputpermissions'): utils.rmtree('inputpermissions') request.addfinalizer(fin_remove_additional_folders) @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_generate_files_nontemplated_exception(): with pytest.raises(exceptions.NonTemplatedInputDirException): generate.generate_files( context={ 'cookiecutter': {'food': 'pizza'} }, repo_dir='tests/test-generate-files-nontemplated' ) @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_generate_files(): generate.generate_files( context={ 'cookiecutter': {'food': 'pizzä'} }, repo_dir='tests/test-generate-files' ) simple_file = 'inputpizzä/simple.txt' assert os.path.isfile(simple_file) simple_text = io.open(simple_file, 'rt', encoding='utf-8').read() assert simple_text == u'I eat pizzä' @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_generate_files_with_trailing_newline(): generate.generate_files( context={ 'cookiecutter': {'food': 'pizzä'} }, repo_dir='tests/test-generate-files' ) newline_file = 'inputpizzä/simple-with-newline.txt' assert os.path.isfile(newline_file) with io.open(newline_file, 'r', encoding='utf-8') as f: simple_text = f.read() assert simple_text == u'I eat pizzä\n' @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_generate_files_binaries(): generate.generate_files( context={ 'cookiecutter': {'binary_test': 'binary_files'} }, repo_dir='tests/test-generate-binaries' ) assert os.path.isfile('inputbinary_files/logo.png') assert os.path.isfile('inputbinary_files/.DS_Store') assert os.path.isfile('inputbinary_files/readme.txt') assert os.path.isfile('inputbinary_files/some_font.otf') assert os.path.isfile('inputbinary_files/binary_files/logo.png') assert os.path.isfile('inputbinary_files/binary_files/.DS_Store') assert os.path.isfile('inputbinary_files/binary_files/readme.txt') assert os.path.isfile('inputbinary_files/binary_files/some_font.otf') assert os.path.isfile( 'inputbinary_files/binary_files/binary_files/logo.png' ) @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_generate_files_absolute_path(): generate.generate_files( context={ 'cookiecutter': {'food': 'pizzä'} }, repo_dir=os.path.abspath('tests/test-generate-files') ) assert os.path.isfile('inputpizzä/simple.txt') @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_generate_files_output_dir(): os.mkdir('tests/custom_output_dir') generate.generate_files( context={ 'cookiecutter': {'food': 'pizzä'} }, repo_dir=os.path.abspath('tests/test-generate-files'), output_dir='tests/custom_output_dir' ) assert os.path.isfile('tests/custom_output_dir/inputpizzä/simple.txt') @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_return_rendered_project_dir(): os.mkdir('tests/custom_output_dir') project_dir = generate.generate_files( context={ 'cookiecutter': {'food': 'pizzä'} }, repo_dir=os.path.abspath('tests/test-generate-files'), output_dir='tests/custom_output_dir' ) assert project_dir == os.path.abspath( 'tests/custom_output_dir/inputpizzä/' ) @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_generate_files_permissions(): """ simple.txt and script.sh should retain their respective 0o644 and 0o755 permissions """ generate.generate_files( context={ 'cookiecutter': {'permissions': 'permissions'} }, repo_dir='tests/test-generate-files-permissions' ) assert os.path.isfile('inputpermissions/simple.txt') # simple.txt should still be 0o644 tests_simple_file = os.path.join( 'tests', 'test-generate-files-permissions', 'input{{cookiecutter.permissions}}', 'simple.txt' ) tests_simple_file_mode = os.stat(tests_simple_file).st_mode & 0o777 input_simple_file = os.path.join( 'inputpermissions', 'simple.txt' ) input_simple_file_mode = os.stat(input_simple_file).st_mode & 0o777 assert tests_simple_file_mode == input_simple_file_mode assert os.path.isfile('inputpermissions/script.sh') # script.sh should still be 0o755 tests_script_file = os.path.join( 'tests', 'test-generate-files-permissions', 'input{{cookiecutter.permissions}}', 'script.sh' ) tests_script_file_mode = os.stat(tests_script_file).st_mode & 0o777 input_script_file = os.path.join( 'inputpermissions', 'script.sh' ) input_script_file_mode = os.stat(input_script_file).st_mode & 0o777 assert tests_script_file_mode == input_script_file_mode cookiecutter-1.3.0/tests/test_generate_hooks.py000066400000000000000000000105261262047200400217640ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_generate_hooks ------------------- Tests formerly known from a unittest residing in test_generate.py named TestHooks.test_ignore_hooks_dirs TestHooks.test_run_python_hooks TestHooks.test_run_python_hooks_cwd TestHooks.test_run_shell_hooks """ from __future__ import unicode_literals import os import sys import stat import pytest from cookiecutter import generate from cookiecutter import utils @pytest.fixture(scope='function') def remove_additional_folders(request): """ Remove some special folders which are created by the tests. """ def fin_remove_additional_folders(): if os.path.exists('tests/test-pyhooks/inputpyhooks'): utils.rmtree('tests/test-pyhooks/inputpyhooks') if os.path.exists('inputpyhooks'): utils.rmtree('inputpyhooks') if os.path.exists('tests/test-shellhooks'): utils.rmtree('tests/test-shellhooks') request.addfinalizer(fin_remove_additional_folders) @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_ignore_hooks_dirs(): generate.generate_files( context={ 'cookiecutter': {'pyhooks': 'pyhooks'} }, repo_dir='tests/test-pyhooks/', output_dir='tests/test-pyhooks/' ) assert not os.path.exists('tests/test-pyhooks/inputpyhooks/hooks') @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_run_python_hooks(): generate.generate_files( context={ 'cookiecutter': {'pyhooks': 'pyhooks'} }, repo_dir='tests/test-pyhooks/'.replace("/", os.sep), output_dir='tests/test-pyhooks/'.replace("/", os.sep) ) assert os.path.exists('tests/test-pyhooks/inputpyhooks/python_pre.txt') assert os.path.exists('tests/test-pyhooks/inputpyhooks/python_post.txt') @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_run_python_hooks_cwd(): generate.generate_files( context={ 'cookiecutter': {'pyhooks': 'pyhooks'} }, repo_dir='tests/test-pyhooks/' ) assert os.path.exists('inputpyhooks/python_pre.txt') assert os.path.exists('inputpyhooks/python_post.txt') def make_test_repo(name): hooks = os.path.join(name, 'hooks') template = os.path.join(name, 'input{{cookiecutter.shellhooks}}') os.mkdir(name) os.mkdir(hooks) os.mkdir(template) with open(os.path.join(template, 'README.rst'), 'w') as f: f.write("foo\n===\n\nbar\n") if sys.platform.startswith('win'): filename = os.path.join(hooks, 'pre_gen_project.bat') with open(filename, 'w') as f: f.write("@echo off\n") f.write("\n") f.write("echo pre generation hook\n") f.write("echo. >shell_pre.txt\n") filename = os.path.join(hooks, 'post_gen_project.bat') with open(filename, 'w') as f: f.write("@echo off\n") f.write("\n") f.write("echo post generation hook\n") f.write("echo. >shell_post.txt\n") else: filename = os.path.join(hooks, 'pre_gen_project.sh') with open(filename, 'w') as f: f.write("#!/bin/bash\n") f.write("\n") f.write("echo 'pre generation hook';\n") f.write("touch 'shell_pre.txt'\n") # Set the execute bit os.chmod(filename, os.stat(filename).st_mode | stat.S_IXUSR) filename = os.path.join(hooks, 'post_gen_project.sh') with open(filename, 'w') as f: f.write("#!/bin/bash\n") f.write("\n") f.write("echo 'post generation hook';\n") f.write("touch 'shell_post.txt'\n") # Set the execute bit os.chmod(filename, os.stat(filename).st_mode | stat.S_IXUSR) @pytest.mark.usefixtures('clean_system', 'remove_additional_folders') def test_run_shell_hooks(): make_test_repo('tests/test-shellhooks') generate.generate_files( context={ 'cookiecutter': {'shellhooks': 'shellhooks'} }, repo_dir='tests/test-shellhooks/', output_dir='tests/test-shellhooks/' ) shell_pre_file = 'tests/test-shellhooks/inputshellhooks/shell_pre.txt' shell_post_file = 'tests/test-shellhooks/inputshellhooks/shell_post.txt' assert os.path.exists(shell_pre_file) assert os.path.exists(shell_post_file) cookiecutter-1.3.0/tests/test_get_config.py000066400000000000000000000045641262047200400211000ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_get_config --------------- Tests formerly known from a unittest residing in test_config.py named TestGetConfig.test_get_config TestGetConfig.test_get_config_does_not_exist TestGetConfig.test_invalid_config TestGetConfigWithDefaults.test_get_config_with_defaults """ import os import pytest from cookiecutter import config from cookiecutter.exceptions import ( ConfigDoesNotExistException, InvalidConfiguration ) def test_get_config(): """ Opening and reading config file """ conf = config.get_config('tests/test-config/valid-config.yaml') expected_conf = { 'cookiecutters_dir': '/home/example/some-path-to-templates', 'replay_dir': '/home/example/some-path-to-replay-files', 'default_context': { 'full_name': 'Firstname Lastname', 'email': 'firstname.lastname@gmail.com', 'github_username': 'example' } } assert conf == expected_conf def test_get_config_does_not_exist(): """ Check that `exceptions.ConfigDoesNotExistException` is raised when attempting to get a non-existent config file. """ with pytest.raises(ConfigDoesNotExistException): config.get_config('tests/test-config/this-does-not-exist.yaml') def test_invalid_config(): """ An invalid config file should raise an `InvalidConfiguration` exception. """ with pytest.raises(InvalidConfiguration) as excinfo: config.get_config('tests/test-config/invalid-config.yaml') expected_error_msg = ( 'tests/test-config/invalid-config.yaml is not a valid YAML file: ' 'line 1: mapping values are not allowed here' ) assert str(excinfo.value) == expected_error_msg def test_get_config_with_defaults(): """ A config file that overrides 1 of 3 defaults """ conf = config.get_config('tests/test-config/valid-partial-config.yaml') default_cookiecutters_dir = os.path.expanduser('~/.cookiecutters/') default_replay_dir = os.path.expanduser('~/.cookiecutter_replay/') expected_conf = { 'cookiecutters_dir': default_cookiecutters_dir, 'replay_dir': default_replay_dir, 'default_context': { 'full_name': 'Firstname Lastname', 'email': 'firstname.lastname@gmail.com', 'github_username': 'example' } } assert conf == expected_conf cookiecutter-1.3.0/tests/test_get_user_config.py000066400000000000000000000105631262047200400221320ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_get_user_config -------------------- Tests formerly known from a unittest residing in test_config.py named TestGetUserConfig.test_get_user_config_valid TestGetUserConfig.test_get_user_config_invalid TestGetUserConfig.test_get_user_config_nonexistent """ import os import shutil import pytest from cookiecutter import config from cookiecutter.exceptions import InvalidConfiguration @pytest.fixture(scope='module') def user_config_path(): return os.path.expanduser('~/.cookiecutterrc') @pytest.fixture(scope='function') def back_up_rc(request, user_config_path): """ Back up an existing cookiecutter rc and restore it after the test. If ~/.cookiecutterrc is pre-existing, move it to a temp location """ user_config_path_backup = os.path.expanduser('~/.cookiecutterrc.backup') if os.path.exists(user_config_path): shutil.copy(user_config_path, user_config_path_backup) os.remove(user_config_path) def remove_test_rc(): """ Remove the ~/.cookiecutterrc that has been created in the test. """ if os.path.exists(user_config_path): os.remove(user_config_path) def restore_original_rc(): """ If it existed, restore the original ~/.cookiecutterrc """ if os.path.exists(user_config_path_backup): shutil.copy(user_config_path_backup, user_config_path) os.remove(user_config_path_backup) # According to the py.test source code finalizers are popped from an # internal list that we populated via 'addfinalizer'. As a result the # last-added finalizer function is executed first. request.addfinalizer(restore_original_rc) request.addfinalizer(remove_test_rc) @pytest.mark.usefixtures('back_up_rc') def test_get_user_config_valid(user_config_path): """ Get config from a valid ~/.cookiecutterrc file """ shutil.copy('tests/test-config/valid-config.yaml', user_config_path) conf = config.get_user_config() expected_conf = { 'cookiecutters_dir': '/home/example/some-path-to-templates', 'replay_dir': '/home/example/some-path-to-replay-files', 'default_context': { 'full_name': 'Firstname Lastname', 'email': 'firstname.lastname@gmail.com', 'github_username': 'example' } } assert conf == expected_conf @pytest.mark.usefixtures('back_up_rc') def test_get_user_config_invalid(user_config_path): """ Get config from an invalid ~/.cookiecutterrc file """ shutil.copy('tests/test-config/invalid-config.yaml', user_config_path) with pytest.raises(InvalidConfiguration): config.get_user_config() @pytest.mark.usefixtures('back_up_rc') def test_get_user_config_nonexistent(): """ Get config from a nonexistent ~/.cookiecutterrc file """ assert config.get_user_config() == config.DEFAULT_CONFIG @pytest.fixture def custom_config(): return { 'cookiecutters_dir': '/foo/bar/some-path-to-templates', 'replay_dir': '/foo/bar/some-path-to-replay-files', 'default_context': { 'full_name': 'Cookiemonster', 'github_username': 'hackebrot' }, 'abbreviations': { 'cookiedozer': 'https://github.com/hackebrot/cookiedozer.git', } } @pytest.fixture def custom_config_path(tmpdir, custom_config): user_config_file = tmpdir.join('user_config') user_config_file.write(config.yaml.dump(custom_config)) return str(user_config_file) def test_specify_config_path(mocker, custom_config_path, custom_config): spy_get_config = mocker.spy(config, 'get_config') user_config = config.get_user_config(custom_config_path) spy_get_config.assert_called_once_with(custom_config_path) assert user_config == custom_config def test_default_config_path(user_config_path): assert config.USER_CONFIG_PATH == user_config_path def test_default_config_from_env_variable( monkeypatch, custom_config_path, custom_config): monkeypatch.setenv('COOKIECUTTER_CONFIG', custom_config_path) user_config = config.get_user_config() assert user_config == custom_config def test_force_default_config(mocker): spy_get_config = mocker.spy(config, 'get_config') user_config = config.get_user_config(None) assert user_config == config.DEFAULT_CONFIG assert not spy_get_config.called cookiecutter-1.3.0/tests/test_hooks.py000077500000000000000000000143041262047200400201130ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_hooks ------------ Tests for `cookiecutter.hooks` module. """ import sys import os import stat import pytest from cookiecutter import hooks, utils, exceptions def make_test_repo(name): """Helper function which is called in the test setup methods.""" hook_dir = os.path.join(name, 'hooks') template = os.path.join(name, 'input{{hooks}}') os.mkdir(name) os.mkdir(hook_dir) os.mkdir(template) with open(os.path.join(template, 'README.rst'), 'w') as f: f.write("foo\n===\n\nbar\n") with open(os.path.join(hook_dir, 'pre_gen_project.py'), 'w') as f: f.write("#!/usr/bin/env python\n") f.write("# -*- coding: utf-8 -*-\n") f.write("from __future__ import print_function\n") f.write("\n") f.write("print('pre generation hook')\n") f.write("f = open('python_pre.txt', 'w')\n") f.write("f.close()\n") if sys.platform.startswith('win'): post = 'post_gen_project.bat' with open(os.path.join(hook_dir, post), 'w') as f: f.write("@echo off\n") f.write("\n") f.write("echo post generation hook\n") f.write("echo. >shell_post.txt\n") else: post = 'post_gen_project.sh' filename = os.path.join(hook_dir, post) with open(filename, 'w') as f: f.write("#!/bin/bash\n") f.write("\n") f.write("echo 'post generation hook';\n") f.write("touch 'shell_post.txt'\n") # Set the execute bit os.chmod(filename, os.stat(filename).st_mode | stat.S_IXUSR) return post class TestFindHooks(object): repo_path = 'tests/test-hooks' def setup_method(self, method): self.post_hook = make_test_repo(self.repo_path) def teardown_method(self, method): utils.rmtree(self.repo_path) def test_find_hook(self): """Finds the specified hook.""" with utils.work_in(self.repo_path): expected = { 'pre_gen_project': os.path.abspath('hooks/pre_gen_project.py'), 'post_gen_project': os.path.abspath( os.path.join('hooks', self.post_hook) ), } assert expected == hooks.find_hooks() def test_no_hooks(self): """find_hooks should return None if the hook could not be found.""" with utils.work_in('tests/fake-repo'): assert {} == hooks.find_hooks() class TestExternalHooks(object): repo_path = os.path.abspath('tests/test-hooks/') hooks_path = os.path.abspath('tests/test-hooks/hooks') def setup_method(self, method): self.post_hook = make_test_repo(self.repo_path) def teardown_method(self, method): utils.rmtree(self.repo_path) if os.path.exists('python_pre.txt'): os.remove('python_pre.txt') if os.path.exists('shell_post.txt'): os.remove('shell_post.txt') if os.path.exists('tests/shell_post.txt'): os.remove('tests/shell_post.txt') if os.path.exists('tests/test-hooks/input{{hooks}}/python_pre.txt'): os.remove('tests/test-hooks/input{{hooks}}/python_pre.txt') if os.path.exists('tests/test-hooks/input{{hooks}}/shell_post.txt'): os.remove('tests/test-hooks/input{{hooks}}/shell_post.txt') if os.path.exists('tests/context_post.txt'): os.remove('tests/context_post.txt') def test_run_script(self): """Execute a hook script, independently of project generation""" hooks.run_script(os.path.join(self.hooks_path, self.post_hook)) assert os.path.isfile('shell_post.txt') def test_run_script_cwd(self): """Change directory before running hook""" hooks.run_script( os.path.join(self.hooks_path, self.post_hook), 'tests' ) assert os.path.isfile('tests/shell_post.txt') assert 'tests' not in os.getcwd() def test_run_script_with_context(self): """Execute a hook script, passing a context""" hook_path = os.path.join(self.hooks_path, 'post_gen_project.sh') if sys.platform.startswith('win'): post = 'post_gen_project.bat' with open(os.path.join(self.hooks_path, post), 'w') as f: f.write("@echo off\n") f.write("\n") f.write("echo post generation hook\n") f.write("echo. >{{cookiecutter.file}}\n") else: with open(hook_path, 'w') as fh: fh.write("#!/bin/bash\n") fh.write("\n") fh.write("echo 'post generation hook';\n") fh.write("touch 'shell_post.txt'\n") fh.write("touch '{{cookiecutter.file}}'\n") os.chmod(hook_path, os.stat(hook_path).st_mode | stat.S_IXUSR) hooks.run_script_with_context( os.path.join(self.hooks_path, self.post_hook), 'tests', { 'cookiecutter': { 'file': 'context_post.txt' } }) assert os.path.isfile('tests/context_post.txt') assert 'tests' not in os.getcwd() def test_run_hook(self): """Execute hook from specified template in specified output directory. """ tests_dir = os.path.join(self.repo_path, 'input{{hooks}}') with utils.work_in(self.repo_path): hooks.run_hook('pre_gen_project', tests_dir, {}) assert os.path.isfile(os.path.join(tests_dir, 'python_pre.txt')) hooks.run_hook('post_gen_project', tests_dir, {}) assert os.path.isfile(os.path.join(tests_dir, 'shell_post.txt')) def test_run_failing_hook(self): hook_path = os.path.join(self.hooks_path, 'pre_gen_project.py') tests_dir = os.path.join(self.repo_path, 'input{{hooks}}') with open(hook_path, 'w') as f: f.write("#!/usr/bin/env python\n") f.write("import sys; sys.exit(1)\n") with utils.work_in(self.repo_path): with pytest.raises(exceptions.FailedHookException) as excinfo: hooks.run_hook('pre_gen_project', tests_dir, {}) assert 'Hook script failed' in str(excinfo.value) cookiecutter-1.3.0/tests/test_identify_repo.py000077500000000000000000000017431262047200400216330ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_identify_repo ------------------ """ import pytest from cookiecutter import exceptions, vcs def test_identify_git_github(): repo_url = 'https://github.com/audreyr/cookiecutter-pypackage.git' assert vcs.identify_repo(repo_url)[0] == 'git' def test_identify_git_github_no_extension(): repo_url = 'https://github.com/audreyr/cookiecutter-pypackage' assert vcs.identify_repo(repo_url)[0] == 'git' def test_identify_git_gitorious(): repo_url = ( 'git@gitorious.org:cookiecutter-gitorious/cookiecutter-gitorious.git' ) assert vcs.identify_repo(repo_url)[0] == 'git' def test_identify_hg_mercurial(): repo_url = 'https://audreyr@bitbucket.org/audreyr/cookiecutter-bitbucket' assert vcs.identify_repo(repo_url)[0] == 'hg' def test_unknown_repo_type(): repo_url = 'http://norepotypespecified.com' with pytest.raises(exceptions.UnknownRepoType): vcs.identify_repo(repo_url)[0] cookiecutter-1.3.0/tests/test_is_vcs_installed.py000077500000000000000000000004611262047200400223140ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_is_vcs_installed --------------------- """ from cookiecutter import vcs def test_existing_repo_type(): assert vcs.is_vcs_installed("git") def test_non_existing_repo_type(): assert not vcs.is_vcs_installed("stringthatisntashellcommand") cookiecutter-1.3.0/tests/test_main.py000066400000000000000000000020731262047200400177110ustar00rootroot00000000000000from cookiecutter.main import is_repo_url, expand_abbreviations def test_is_repo_url(): """Verify is_repo_url works.""" assert is_repo_url('gitolite@server:team/repo') is True assert is_repo_url('git@github.com:audreyr/cookiecutter.git') is True assert is_repo_url('https://github.com/audreyr/cookiecutter.git') is True assert is_repo_url('https://bitbucket.org/pokoli/cookiecutter.hg') is True assert is_repo_url('/audreyr/cookiecutter.git') is False assert is_repo_url('/home/audreyr/cookiecutter') is False appveyor_temp_dir = ( 'c:\\users\\appveyor\\appdata\\local\\temp\\1\\pytest-0\\' 'test_default_output_dir0\\template' ) assert is_repo_url(appveyor_temp_dir) is False def test_expand_abbreviations(): template = 'gh:audreyr/cookiecutter-pypackage' # This is not a valid repo url just yet! # First `main.expand_abbreviations` needs to translate it assert is_repo_url(template) is False expanded_template = expand_abbreviations(template, {}) assert is_repo_url(expanded_template) is True cookiecutter-1.3.0/tests/test_more_cookiecutters.py000066400000000000000000000042371262047200400226760ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_more_cookiecutters ----------------------- Test formerly known from a unittest residing in test_examples.py named TestGitBranch.test_branch TestExamplesRepoArg.test_cookiecutter_pypackage_git """ from __future__ import unicode_literals import os import sys import subprocess import pytest from cookiecutter import config, utils from tests.skipif_markers import skipif_travis, skipif_no_network @pytest.fixture(scope='function') def remove_additional_dirs(request): """ Remove special directories which are creating during the tests. """ def fin_remove_additional_dirs(): with utils.work_in(config.DEFAULT_CONFIG['cookiecutters_dir']): if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('boilerplate'): utils.rmtree('boilerplate') if os.path.isdir('python_boilerplate'): utils.rmtree('python_boilerplate') request.addfinalizer(fin_remove_additional_dirs) @skipif_travis @skipif_no_network @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') def test_git_branch(): pypackage_git = 'https://github.com/audreyr/cookiecutter-pypackage.git' proc = subprocess.Popen( '{0} -m cookiecutter.cli -c console-script {1}'.format(sys.executable, pypackage_git), stdin=subprocess.PIPE, shell=True ) # Just skip all the prompts proc.communicate(input=b'\n\n\n\n\n\n\n\n\n\n\n\n') assert os.path.isfile('boilerplate/README.rst') assert os.path.isfile('boilerplate/boilerplate/main.py') @skipif_travis @skipif_no_network @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') def test_cookiecutter_pypackage_git(): proc = subprocess.Popen( '{0} -m cookiecutter.cli https://github.com/audreyr/' 'cookiecutter-pypackage.git'.format(sys.executable), stdin=subprocess.PIPE, shell=True ) # Just skip all the prompts proc.communicate(input=b'\n\n\n\n\n\n\n\n\n\n\n\n') assert os.path.isfile('python_boilerplate/README.rst') cookiecutter-1.3.0/tests/test_output_folder.py000066400000000000000000000037071262047200400216650ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_output_folder ------------------ Test formerly known from a unittest residing in test_generate.py named TestOutputFolder.test_output_folder """ from __future__ import unicode_literals import os import pytest from cookiecutter import generate from cookiecutter import utils from cookiecutter import exceptions @pytest.fixture(scope='function') def remove_output_folder(request): """ Remove the output folder in case it exists on disk. """ def finalizer_remove_output_folder(): if os.path.exists('output_folder'): utils.rmtree('output_folder') request.addfinalizer(finalizer_remove_output_folder) @pytest.mark.usefixtures('clean_system', 'remove_output_folder') def test_output_folder(): context = generate.generate_context( context_file='tests/test-output-folder/cookiecutter.json' ) generate.generate_files( context=context, repo_dir='tests/test-output-folder' ) something = """Hi! My name is Audrey Greenfeld. It is 2014.""" something2 = open('output_folder/something.txt').read() assert something == something2 in_folder = "The color is green and the letter is D." in_folder2 = open('output_folder/folder/in_folder.txt').read() assert in_folder == in_folder2 assert os.path.isdir('output_folder/im_a.dir') assert os.path.isfile('output_folder/im_a.dir/im_a.file.py') @pytest.mark.usefixtures('clean_system', 'remove_output_folder') def test_exception_when_output_folder_exists(): context = generate.generate_context( context_file='tests/test-output-folder/cookiecutter.json' ) output_folder = context['cookiecutter']['test_name'] if not os.path.exists(output_folder): os.makedirs(output_folder) with pytest.raises(exceptions.OutputDirExistsException): generate.generate_files( context=context, repo_dir='tests/test-output-folder' ) cookiecutter-1.3.0/tests/test_preferred_encoding.py000066400000000000000000000014611262047200400226110ustar00rootroot00000000000000# -*- coding: utf-8 -*- import locale import codecs import pytest import sys PY3 = sys.version_info[0] == 3 @pytest.mark.skipif(not PY3, reason='Only necessary on Python3') def test_not_ascii(): """Make sure that the systems preferred encoding is not `ascii`. Otherwise `click` is raising a RuntimeError for Python3. For a detailed description of this very problem please consult the following gist: https://gist.github.com/hackebrot/937245251887197ef542 This test also checks that `tox.ini` explicitly copies the according system environment variables to the test environments. """ try: preferred_encoding = locale.getpreferredencoding() fs_enc = codecs.lookup(preferred_encoding).name except Exception: fs_enc = 'ascii' assert fs_enc != 'ascii' cookiecutter-1.3.0/tests/test_prompt.py000066400000000000000000000200171262047200400203040ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_prompt ----------- Tests for `cookiecutter.prompt` module. """ from collections import OrderedDict import platform import pytest from past.builtins import basestring from cookiecutter import prompt from jinja2.environment import Environment @pytest.mark.parametrize('raw_var, rendered_var', [ (1, '1'), (True, 'True'), ('foo', 'foo'), ('{{cookiecutter.project}}', 'foobar') ]) def test_convert_to_str(mocker, raw_var, rendered_var): env = Environment() from_string = mocker.patch( 'cookiecutter.prompt.Environment.from_string', wraps=env.from_string ) context = {'project': 'foobar'} result = prompt.render_variable(env, raw_var, context) assert result == rendered_var # Make sure that non str variables are conerted beforehand if not isinstance(raw_var, basestring): raw_var = str(raw_var) from_string.assert_called_once_with(raw_var) @pytest.fixture(autouse=True) def patch_readline_on_win(monkeypatch): if 'windows' in platform.platform().lower(): monkeypatch.setattr('sys.stdin.readline', lambda: '\n') class TestPrompt(object): def test_prompt_for_config_simple(self, monkeypatch): monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: u'Audrey Roy' ) context = {'cookiecutter': {'full_name': 'Your Name'}} cookiecutter_dict = prompt.prompt_for_config(context) assert cookiecutter_dict == {'full_name': u'Audrey Roy'} def test_prompt_for_config_unicode(self, monkeypatch): monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: u'Pizzä ïs Gööd' ) context = {'cookiecutter': {'full_name': 'Your Name'}} cookiecutter_dict = prompt.prompt_for_config(context) assert cookiecutter_dict == {'full_name': u'Pizzä ïs Gööd'} def test_unicode_prompt_for_config_unicode(self, monkeypatch): monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: u'Pizzä ïs Gööd' ) context = {'cookiecutter': {'full_name': u'Řekni či napiš své jméno'}} cookiecutter_dict = prompt.prompt_for_config(context) assert cookiecutter_dict == {'full_name': u'Pizzä ïs Gööd'} def test_unicode_prompt_for_default_config_unicode(self, monkeypatch): monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: default ) context = {'cookiecutter': {'full_name': u'Řekni či napiš své jméno'}} cookiecutter_dict = prompt.prompt_for_config(context) assert cookiecutter_dict == {'full_name': u'Řekni či napiš své jméno'} def test_unicode_prompt_for_templated_config(self, monkeypatch): monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: default ) context = {'cookiecutter': OrderedDict([ ( 'project_name', u'A New Project' ), ( 'pkg_name', u'{{ cookiecutter.project_name|lower|replace(" ", "") }}' ) ])} exp_cookiecutter_dict = { 'project_name': u'A New Project', 'pkg_name': u'anewproject' } cookiecutter_dict = prompt.prompt_for_config(context) assert cookiecutter_dict == exp_cookiecutter_dict def test_dont_prompt_for_private_context_var(self, monkeypatch): monkeypatch.setattr( 'cookiecutter.prompt.read_user_variable', lambda var, default: pytest.fail( 'Should not try to read a response for private context var' ) ) context = {'cookiecutter': {'_copy_without_render': ['*.html']}} cookiecutter_dict = prompt.prompt_for_config(context) assert cookiecutter_dict == {'_copy_without_render': ['*.html']} class TestReadUserChoice(object): def test_should_invoke_read_user_choice(self, mocker): prompt_choice = mocker.patch( 'cookiecutter.prompt.prompt_choice_for_config', wraps=prompt.prompt_choice_for_config ) read_choice = mocker.patch('cookiecutter.prompt.read_user_choice') read_choice.return_value = 'all' read_variable = mocker.patch('cookiecutter.prompt.read_user_variable') CHOICES = ['landscape', 'portrait', 'all'] CONTEXT = { 'cookiecutter': { 'orientation': CHOICES } } cookiecutter_dict = prompt.prompt_for_config(CONTEXT) assert not read_variable.called assert prompt_choice.called read_choice.assert_called_once_with('orientation', CHOICES) assert cookiecutter_dict == {'orientation': 'all'} def test_should_not_invoke_read_user_variable(self, mocker): read_variable = mocker.patch('cookiecutter.prompt.read_user_variable') read_variable.return_value = u'Audrey Roy' prompt_choice = mocker.patch( 'cookiecutter.prompt.prompt_choice_for_config' ) read_choice = mocker.patch('cookiecutter.prompt.read_user_choice') CONTEXT = {'cookiecutter': {'full_name': 'Your Name'}} cookiecutter_dict = prompt.prompt_for_config(CONTEXT) assert not prompt_choice.called assert not read_choice.called read_variable.assert_called_once_with('full_name', 'Your Name') assert cookiecutter_dict == {'full_name': u'Audrey Roy'} def test_should_render_choices(self, mocker): read_choice = mocker.patch('cookiecutter.prompt.read_user_choice') read_choice.return_value = u'anewproject' read_variable = mocker.patch('cookiecutter.prompt.read_user_variable') read_variable.return_value = u'A New Project' RENDERED_CHOICES = [ u'foo', u'anewproject', u'bar' ] CONTEXT = {'cookiecutter': OrderedDict([ ( 'project_name', u'A New Project' ), ( 'pkg_name', [ u'foo', u'{{ cookiecutter.project_name|lower|replace(" ", "") }}', u'bar' ] ) ])} EXP_COOKIECUTTER_DICT = { 'project_name': u'A New Project', 'pkg_name': u'anewproject' } cookiecutter_dict = prompt.prompt_for_config(CONTEXT) read_variable.assert_called_once_with('project_name', u'A New Project') read_choice.assert_called_once_with('pkg_name', RENDERED_CHOICES) assert cookiecutter_dict == EXP_COOKIECUTTER_DICT class TestPromptChoiceForConfig(object): @pytest.fixture def choices(self): return ['landscape', 'portrait', 'all'] @pytest.fixture def context(self, choices): return { 'cookiecutter': { 'orientation': choices } } def test_should_return_first_option_if_no_input( self, mocker, choices, context): read_choice = mocker.patch('cookiecutter.prompt.read_user_choice') expected_choice = choices[0] actual_choice = prompt.prompt_choice_for_config( context, Environment(), 'orientation', choices, True # Suppress user input ) assert not read_choice.called assert expected_choice == actual_choice def test_should_read_userchoice(self, mocker, choices, context): read_choice = mocker.patch('cookiecutter.prompt.read_user_choice') read_choice.return_value = 'all' expected_choice = 'all' actual_choice = prompt.prompt_choice_for_config( context, Environment(), 'orientation', choices, False # Ask the user for input ) read_choice.assert_called_once_with('orientation', choices) assert expected_choice == actual_choice cookiecutter-1.3.0/tests/test_read_user_choice.py000066400000000000000000000017201262047200400222460ustar00rootroot00000000000000# -*- coding: utf-8 -*- import click import pytest from cookiecutter.prompt import read_user_choice OPTIONS = ['hello', 'world', 'foo', 'bar'] EXPECTED_PROMPT = """Select varname: 1 - hello 2 - world 3 - foo 4 - bar Choose from 1, 2, 3, 4""" @pytest.mark.parametrize('user_choice, expected_value', enumerate(OPTIONS, 1)) def test_click_invocation(mocker, user_choice, expected_value): choice = mocker.patch('click.Choice') choice.return_value = click.Choice(OPTIONS) prompt = mocker.patch('click.prompt') prompt.return_value = '{}'.format(user_choice) assert read_user_choice('varname', OPTIONS) == expected_value prompt.assert_called_once_with( EXPECTED_PROMPT, type=click.Choice(OPTIONS), default='1' ) def test_raise_if_options_is_not_a_non_empty_list(): with pytest.raises(TypeError): read_user_choice('foo', 'NOT A LIST') with pytest.raises(ValueError): read_user_choice('foo', []) cookiecutter-1.3.0/tests/test_read_user_variable.py000077500000000000000000000007431262047200400226100ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test_read_user_variable ----------------------- """ from __future__ import unicode_literals import click from cookiecutter.prompt import read_user_variable VARIABLE = 'project_name' DEFAULT = 'Kivy Project' def test_click_invocation(mocker): prompt = mocker.patch('click.prompt') prompt.return_value = DEFAULT assert read_user_variable(VARIABLE, DEFAULT) == DEFAULT click.prompt.assert_called_once_with(VARIABLE, default=DEFAULT) cookiecutter-1.3.0/tests/test_read_user_yes_no.py000066400000000000000000000010301262047200400223020ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test_read_user_yes_no --------------------- """ from __future__ import unicode_literals import click from cookiecutter.prompt import read_user_yes_no QUESTION = 'Is it okay to delete and re-clone it?' DEFAULT = 'y' def test_click_invocation(mocker): prompt = mocker.patch('click.prompt') prompt.return_value = DEFAULT assert read_user_yes_no(QUESTION, DEFAULT) == DEFAULT click.prompt.assert_called_once_with( QUESTION, default=DEFAULT, type=click.BOOL ) cookiecutter-1.3.0/tests/test_specify_output_dir.py000066400000000000000000000031211262047200400227000ustar00rootroot00000000000000# -*- coding: utf-8 -*- import pytest from cookiecutter import main @pytest.fixture def context(): """Fixture to return a valid context as known from a cookiecutter.json.""" return { u'cookiecutter': { u'email': u'raphael@hackebrot.de', u'full_name': u'Raphael Pierzina', u'github_username': u'hackebrot', u'version': u'0.1.0', } } @pytest.fixture def output_dir(tmpdir): return str(tmpdir.mkdir('output')) @pytest.fixture def template(tmpdir): return str(tmpdir.mkdir('template')) @pytest.fixture(autouse=True) def mock_gen_context(mocker, context): mocker.patch('cookiecutter.main.generate_context', return_value=context) @pytest.fixture(autouse=True) def mock_prompt(mocker): mocker.patch('cookiecutter.main.prompt_for_config') @pytest.fixture(autouse=True) def mock_replay(mocker): mocker.patch('cookiecutter.main.dump') def test_api_invocation(mocker, template, output_dir, context): mock_gen_files = mocker.patch('cookiecutter.main.generate_files') main.cookiecutter(template, output_dir=output_dir) mock_gen_files.assert_called_once_with( repo_dir=template, context=context, overwrite_if_exists=False, output_dir=output_dir ) def test_default_output_dir(mocker, template, context): mock_gen_files = mocker.patch('cookiecutter.main.generate_files') main.cookiecutter(template) mock_gen_files.assert_called_once_with( repo_dir=template, context=context, overwrite_if_exists=False, output_dir='.' ) cookiecutter-1.3.0/tests/test_utils.py000077500000000000000000000033111262047200400201240ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_utils ------------ Tests for `cookiecutter.utils` module. """ import os import pytest import stat import sys from cookiecutter import utils def make_readonly(path): """Helper function that is called in the tests to change the access permissions of the given file. """ mode = os.stat(path).st_mode os.chmod(path, mode & ~stat.S_IWRITE) def test_rmtree(): os.mkdir('foo') with open('foo/bar', "w") as f: f.write("Test data") make_readonly('foo/bar') utils.rmtree('foo') assert not os.path.exists('foo') def test_make_sure_path_exists(): if sys.platform.startswith('win'): existing_directory = os.path.abspath(os.curdir) uncreatable_directory = 'a*b' else: existing_directory = '/usr/' uncreatable_directory = '/this-doesnt-exist-and-cant-be-created/' assert utils.make_sure_path_exists(existing_directory) assert utils.make_sure_path_exists('tests/blah') assert utils.make_sure_path_exists('tests/trailingslash/') assert not utils.make_sure_path_exists(uncreatable_directory) utils.rmtree('tests/blah/') utils.rmtree('tests/trailingslash/') def test_workin(): cwd = os.getcwd() ch_to = 'tests/files' class TestException(Exception): pass def test_work_in(): with utils.work_in(ch_to): test_dir = os.path.join(cwd, ch_to).replace("/", os.sep) assert test_dir == os.getcwd() raise TestException() # Make sure we return to the correct folder assert cwd == os.getcwd() # Make sure that exceptions are still bubbled up with pytest.raises(TestException): test_work_in() cookiecutter-1.3.0/tests/test_vcs.py000077500000000000000000000104631262047200400175650ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_vcs ------------ Tests for `cookiecutter.vcs` module. """ import locale import os import pytest import subprocess from cookiecutter import exceptions, utils, vcs from tests.skipif_markers import skipif_no_network ENCODING = locale.getdefaultlocale()[1] @skipif_no_network def test_git_clone(): repo_dir = vcs.clone( 'https://github.com/audreyr/cookiecutter-pypackage.git' ) assert repo_dir == 'cookiecutter-pypackage' assert os.path.isfile('cookiecutter-pypackage/README.rst') if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') @skipif_no_network def test_git_clone_with_trailing_slash(): repo_dir = vcs.clone( 'https://github.com/audreyr/cookiecutter-pypackage/' ) assert repo_dir == 'cookiecutter-pypackage' assert os.path.isfile('cookiecutter-pypackage/README.rst') if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') @skipif_no_network def test_git_clone_checkout(): repo_dir = vcs.clone( 'https://github.com/audreyr/cookiecutter-pypackage.git', 'console-script' ) git_dir = 'cookiecutter-pypackage' assert repo_dir == git_dir assert os.path.isfile(os.path.join('cookiecutter-pypackage', 'README.rst')) proc = subprocess.Popen( ['git', 'symbolic-ref', 'HEAD'], cwd=git_dir, stdout=subprocess.PIPE ) symbolic_ref = proc.communicate()[0] branch = symbolic_ref.decode(ENCODING).strip().split('/')[-1] assert 'console-script' == branch if os.path.isdir(git_dir): utils.rmtree(git_dir) @skipif_no_network def test_git_clone_custom_dir(): os.makedirs('tests/custom_dir1/custom_dir2/') repo_dir = vcs.clone( repo_url='https://github.com/audreyr/cookiecutter-pypackage.git', checkout=None, clone_to_dir='tests/custom_dir1/custom_dir2/' ) with utils.work_in('tests/custom_dir1/custom_dir2/'): test_dir = 'tests/custom_dir1/custom_dir2/cookiecutter-pypackage' assert repo_dir == test_dir.replace('/', os.sep) assert os.path.isfile('cookiecutter-pypackage/README.rst') if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('tests/custom_dir1'): utils.rmtree('tests/custom_dir1') @skipif_no_network def test_hg_clone(): repo_dir = vcs.clone( 'https://bitbucket.org/pokoli/cookiecutter-trytonmodule' ) assert repo_dir == 'cookiecutter-trytonmodule' assert os.path.isfile('cookiecutter-trytonmodule/README.rst') if os.path.isdir('cookiecutter-trytonmodule'): utils.rmtree('cookiecutter-trytonmodule') @skipif_no_network def test_vcs_not_installed(monkeypatch): monkeypatch.setattr( 'cookiecutter.vcs.identify_repo', lambda x: (u'stringthatisntashellcommand', u'anotherstring'), ) with pytest.raises(exceptions.VCSNotInstalled): vcs.clone('http://norepotypespecified.com') @pytest.mark.parametrize('repo_url, exp_repo_type, exp_repo_url', [ ( "git+https://github.com/pytest-dev/cookiecutter-pytest-plugin.git", "git", "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git" ), ( "hg+https://bitbucket.org/foo/bar.hg", "hg", "https://bitbucket.org/foo/bar.hg" ), ( "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git", "git", "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git" ), ( "https://bitbucket.org/foo/bar.hg", "hg", "https://bitbucket.org/foo/bar.hg" ) ]) def test_identify_known_repo(repo_url, exp_repo_type, exp_repo_url): assert vcs.identify_repo(repo_url) == (exp_repo_type, exp_repo_url) @pytest.fixture(params=[ "foo+git", # uses explicit identifier with 'git' in the wrong place "foo+hg", # uses explicit identifier with 'hg' in the wrong place "foo+bar", # uses explicit identifier with neither 'git' nor 'hg' "foobar" # no identifier but neither 'git' nor 'bitbucket' in url ]) def unknown_repo_type_url(request): return request.param def test_identify_raise_on_unknown_repo(unknown_repo_type_url): with pytest.raises(exceptions.UnknownRepoType): vcs.identify_repo(unknown_repo_type_url) cookiecutter-1.3.0/tests/test_vcs_prompt.py000077500000000000000000000047071262047200400211720ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ test_vcs_prompt --------------- """ import os import pytest from cookiecutter import utils, vcs from tests.skipif_markers import skipif_no_network @pytest.fixture(autouse=True) def clean_cookiecutter_dirs(request): if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') os.mkdir('cookiecutter-pypackage/') if os.path.isdir('cookiecutter-trytonmodule'): utils.rmtree('cookiecutter-trytonmodule') os.mkdir('cookiecutter-trytonmodule/') def remove_cookiecutter_dirs(): if os.path.isdir('cookiecutter-pypackage'): utils.rmtree('cookiecutter-pypackage') if os.path.isdir('cookiecutter-trytonmodule'): utils.rmtree('cookiecutter-trytonmodule') request.addfinalizer(remove_cookiecutter_dirs) @skipif_no_network def test_git_clone_overwrite(monkeypatch): monkeypatch.setattr( 'cookiecutter.vcs.read_user_yes_no', lambda question, default: True ) repo_dir = vcs.clone( 'https://github.com/audreyr/cookiecutter-pypackage.git' ) assert repo_dir == 'cookiecutter-pypackage' assert os.path.isfile('cookiecutter-pypackage/README.rst') @skipif_no_network def test_git_clone_overwrite_with_no_prompt(): repo_dir = vcs.clone( 'https://github.com/audreyr/cookiecutter-pypackage.git', no_input=True ) assert repo_dir == 'cookiecutter-pypackage' assert os.path.isfile('cookiecutter-pypackage/README.rst') @skipif_no_network def test_git_clone_cancel(monkeypatch): monkeypatch.setattr( 'cookiecutter.vcs.read_user_yes_no', lambda question, default: False ) with pytest.raises(SystemExit): vcs.clone('https://github.com/audreyr/cookiecutter-pypackage.git') @skipif_no_network def test_hg_clone_overwrite(monkeypatch): monkeypatch.setattr( 'cookiecutter.vcs.read_user_yes_no', lambda question, default: True ) repo_dir = vcs.clone( 'https://bitbucket.org/pokoli/cookiecutter-trytonmodule' ) assert repo_dir == 'cookiecutter-trytonmodule' assert os.path.isfile('cookiecutter-trytonmodule/README.rst') @skipif_no_network def test_hg_clone_cancel(monkeypatch): monkeypatch.setattr( 'cookiecutter.vcs.read_user_yes_no', lambda question, default: False ) with pytest.raises(SystemExit): vcs.clone('https://bitbucket.org/pokoli/cookiecutter-trytonmodule') cookiecutter-1.3.0/tox.ini000066400000000000000000000006231262047200400155240ustar00rootroot00000000000000[tox] envlist = py27, py33, py34, py35, pypy, flake8 [testenv] passenv = LC_ALL, LANG, HOME commands = py.test --cov=cookiecutter {posargs:tests} deps = pytest pytest-cov pytest-mock [testenv:flake8] deps = flake8==2.3.0 pep8==1.6.2 commands = flake8 cookiecutter tests setup.py [testenv:cov-report] commands = py.test --cov=cookiecutter --cov-report=term --cov-report=html