From 210cefbadc828b9533f7e09b1351a31a973e437d Mon Sep 17 00:00:00 2001 From: logworthy Date: Thu, 13 May 2021 02:56:20 +1000 Subject: [PATCH 001/202] Log the error message when git clone fails, not just the return code (#1505) Co-authored-by: Lyndon D'Arcy --- cookiecutter/vcs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py index d9af1311c..4a846876e 100644 --- a/cookiecutter/vcs.py +++ b/cookiecutter/vcs.py @@ -115,6 +115,7 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): 'The {} branch of repository {} could not found, ' 'have you made a typo?'.format(checkout, repo_url) ) + logger.error('git clone failed with error: %s', output) raise return repo_dir From 95ebdcb426fbe94a42085328f6591b75f50ce33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20H=C3=BCbner?= <66410120+juhuebner@users.noreply.github.com> Date: Wed, 12 May 2021 19:00:45 +0200 Subject: [PATCH 002/202] fixes prompting private rendered dicts (#1504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixes private dir handling * added patch branch to CI coverage.yml * changed codecov action * get coverage artifact * get artifact 2nd try * 3rd try to get artifacts * 4th try * 5th * Squashed commit of the following: commit f0ac6a093319ad33f818fb1c4329d72a24f61bd7 Author: Jens Hübner Date: Mon Feb 8 23:33:43 2021 +0100 5th commit 2af3902a6d9774a07e21f1d00c1cb1c22a8d7d67 Author: Jens Hübner Date: Mon Feb 8 23:20:18 2021 +0100 4th try commit 903f6741e800f3ba155b73e009f57048bc498b8c Author: Jens Hübner Date: Mon Feb 8 23:07:46 2021 +0100 3rd try to get artifacts commit c8a517888bb494227761ef95c24f7e6bf74934a4 Author: Jens Hübner Date: Mon Feb 8 22:46:45 2021 +0100 get artifact 2nd try commit 287c8ebe478e9b4c15a773b94018cb13729df81c Author: Jens Hübner Date: Mon Feb 8 22:32:58 2021 +0100 get coverage artifact commit 6b11f6abdc97126db11fe45cc7702449be45c3be Author: Jens Hübner Date: Mon Feb 8 22:18:24 2021 +0100 changed codecov action * Squashed commit of the following: commit 661c50b156b5db856f845391104d08340f4d6be1 Author: Jens Hübner Date: Tue Feb 9 19:00:27 2021 +0100 modified coverage for code which is unreachable s.a. https://bugs.python.org/issue39340 commit f9cb8e7a1df21d029e2da17a9742f89d9bea0170 Author: Jens Hübner Date: Tue Feb 9 10:00:16 2021 +0100 added posargs to tox.ini * applied black * cleanup * removed dev leftovers * clean-up docstring --- cookiecutter/prompt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index 90e910e13..dfb8f3218 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -209,7 +209,7 @@ def prompt_for_config(context, no_input=False): # Second pass; handle the dictionaries. for key, raw in context['cookiecutter'].items(): - # Skip private type dicts + # Skip private type dicts not ot be rendered. if key.startswith('_') and not key.startswith('__'): continue @@ -218,7 +218,7 @@ def prompt_for_config(context, no_input=False): # We are dealing with a dict variable val = render_variable(env, raw, cookiecutter_dict) - if not no_input: + if not no_input and not key.startswith('__'): val = read_user_dict(key, val) cookiecutter_dict[key] = val From 802cd705edbcc1c18ab2c8d7f8d9a8163597f9fc Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Thu, 20 May 2021 22:30:00 +0200 Subject: [PATCH 003/202] pre-commit: add bandit hook ref: https://github.com/cookiecutter/cookiecutter/issues/1523 --- .bandit | 3 +++ .pre-commit-config.yaml | 5 +++++ cookiecutter/hooks.py | 4 ++-- cookiecutter/vcs.py | 6 +++--- 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 .bandit diff --git a/.bandit b/.bandit new file mode 100644 index 000000000..49e34d3e5 --- /dev/null +++ b/.bandit @@ -0,0 +1,3 @@ +[bandit] +exclude=tests/* +targets=cookiecutter/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1cc4a786d..c1f2a0886 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,3 +34,8 @@ repos: - flake8-absolute-import - flake8-black - flake8-docstrings + - repo: https://github.com/PyCQA/bandit + rev: 1.6.0 + hooks: + - id: bandit + args: [--ini, .bandit] diff --git a/cookiecutter/hooks.py b/cookiecutter/hooks.py index 3c73f7487..b6a31a1e0 100644 --- a/cookiecutter/hooks.py +++ b/cookiecutter/hooks.py @@ -2,7 +2,7 @@ import errno import logging import os -import subprocess +import subprocess # nosec import sys import tempfile @@ -79,7 +79,7 @@ def run_script(script_path, cwd='.'): utils.make_executable(script_path) try: - proc = subprocess.Popen(script_command, shell=run_thru_shell, cwd=cwd) + proc = subprocess.Popen(script_command, shell=run_thru_shell, cwd=cwd) # nosec exit_status = proc.wait() if exit_status != EXIT_SUCCESS: raise FailedHookException( diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py index 4a846876e..746dfade9 100644 --- a/cookiecutter/vcs.py +++ b/cookiecutter/vcs.py @@ -1,7 +1,7 @@ """Helper functions for working with version control systems.""" import logging import os -import subprocess +import subprocess # nosec from shutil import which from cookiecutter.exceptions import ( @@ -92,13 +92,13 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): if clone: try: - subprocess.check_output( + subprocess.check_output( # nosec [repo_type, 'clone', repo_url], cwd=clone_to_dir, stderr=subprocess.STDOUT, ) if checkout is not None: - subprocess.check_output( + subprocess.check_output( # nosec [repo_type, 'checkout', checkout], cwd=repo_dir, stderr=subprocess.STDOUT, From 7062271cec6d89a564715aab34558a48c90df801 Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Thu, 20 May 2021 23:27:35 +0200 Subject: [PATCH 004/202] add safety ci step Closes: https://github.com/cookiecutter/cookiecutter/issues/1523 --- .github/workflows/main.yml | 2 ++ tox.ini | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 55dcc8756..73c66aff7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,6 +24,8 @@ jobs: pip install tox virtualenv - name: Lint run: "tox -e lint" + - name: Safety + run: "tox -e safety" build: runs-on: ${{ matrix.os }} strategy: diff --git a/tox.ini b/tox.ini index 0d888f608..4ad01238d 100644 --- a/tox.ini +++ b/tox.ini @@ -9,8 +9,8 @@ envlist = minversion = 3.14.2 requires = # https://github.com/tox-dev/tox/issues/765 - virtualenv >= 16.7.9 - pip >= 19.3.1 + virtualenv >= 20.4.5 + pip >= 21.1.1 [testenv] passenv = @@ -45,3 +45,13 @@ commands = whitelist_externals = make deps = -rtest_requirements.txt skip_install = true + +[testenv:safety] +passenv = + LC_ALL + LANG + HOME +commands = + safety check --full-report +deps = + safety \ No newline at end of file From 055260320d1f51dee1847a7acd9b97bfb45b4eca Mon Sep 17 00:00:00 2001 From: Fabio Todaro Date: Fri, 21 May 2021 15:08:54 +0200 Subject: [PATCH 005/202] Force click<8.0.0 --- setup.py | 2 +- tests/test_read_user_dict.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 124dc40c9..654010fa0 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ requirements = [ 'binaryornot>=0.4.4', 'Jinja2>=2.7,<4.0.0', - 'click>=7.0', + 'click>=7.0,<8.0.0', 'pyyaml>=5.3.1', 'jinja2-time>=0.2.0', 'python-slugify>=4.0.0', diff --git a/tests/test_read_user_dict.py b/tests/test_read_user_dict.py index 8d16441e2..ccf632258 100644 --- a/tests/test_read_user_dict.py +++ b/tests/test_read_user_dict.py @@ -97,6 +97,17 @@ def test_should_call_prompt_with_process_json(mocker): ) +def test_should_not_call_process_json_default_value(mocker, monkeypatch): + """Make sure that `process_json` is not called when using default value.""" + mock_process_json = mocker.patch('cookiecutter.prompt.process_json', autospec=True) + + runner = click.testing.CliRunner() + with runner.isolation(input="\n"): + read_user_dict('name', {'project_slug': 'pytest-plugin'}) + + mock_process_json.assert_not_called() + + def test_read_user_dict_default_value(mocker): """Make sure that `read_user_dict` returns the default value. From aa5581eab0a1ef2ebf13da2750c33d5c7a36954a Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Fri, 21 May 2021 15:29:30 +0200 Subject: [PATCH 006/202] ensure filesystem isolation during tests execution Closes: https://github.com/cookiecutter/cookiecutter/issues/517 --- tests/conftest.py | 15 +++++++++++++++ tests/test_cli.py | 4 +--- tests/test_get_config.py | 10 +++++----- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 5f8bbddb4..e21071304 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ import pytest from cookiecutter import utils +from cookiecutter.config import DEFAULT_CONFIG USER_CONFIG = """ @@ -17,6 +18,20 @@ # strings-in-yaml---to-quote-or-not-to-quote.html +@pytest.fixture(autouse=True) +def isolated_filesystem(monkeypatch, tmp_path): + """Ensure filesystem isolation, set the user home to a tmp_path.""" + root_path = tmp_path.joinpath("home") + root_path.mkdir() + cookiecutters_dir = root_path.joinpath(".cookiecutters/") + replay_dir = root_path.joinpath(".cookiecutter_replay/") + monkeypatch.setitem(DEFAULT_CONFIG, 'cookiecutters_dir', str(cookiecutters_dir)) + monkeypatch.setitem(DEFAULT_CONFIG, 'replay_dir', str(replay_dir)) + + monkeypatch.setenv("HOME", str(root_path)) + monkeypatch.setenv("USERPROFILE", str(root_path)) + + def backup_dir(original_dir, backup_dir): """Generate backup directory based on original directory.""" # If the default original_dir is pre-existing, move it to a temp location diff --git a/tests/test_cli.py b/tests/test_cli.py index 0da736ec2..e357fef2e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -181,9 +181,7 @@ def test_run_cookiecutter_on_overwrite_if_exists_and_replay( mocker, cli_runner, overwrite_cli_flag ): """Test cli invocation with `overwrite-if-exists` and `replay` flags.""" - mock_cookiecutter = mocker.patch( - 'cookiecutter.cli.cookiecutter', side_effect=cookiecutter - ) + mock_cookiecutter = mocker.patch('cookiecutter.cli.cookiecutter') template_path = 'tests/fake-repo-pre/' result = cli_runner(template_path, '--replay', '-v', overwrite_cli_flag) diff --git a/tests/test_get_config.py b/tests/test_get_config.py index 3f282a12b..760db3cdb 100644 --- a/tests/test_get_config.py +++ b/tests/test_get_config.py @@ -1,5 +1,5 @@ """Collection of tests around loading cookiecutter config.""" -import os +from pathlib import Path import pytest import yaml @@ -99,11 +99,11 @@ def test_invalid_config(): 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/') + default_cookiecutters_dir = Path('~/.cookiecutters').expanduser() + default_replay_dir = Path('~/.cookiecutter_replay').expanduser() expected_conf = { - 'cookiecutters_dir': default_cookiecutters_dir, - 'replay_dir': default_replay_dir, + 'cookiecutters_dir': str(default_cookiecutters_dir), + 'replay_dir': str(default_replay_dir), 'default_context': { 'full_name': 'Firstname Lastname', 'email': 'firstname.lastname@gmail.com', From 6281290e69d44c5fc20406a72ab581880962e93a Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Fri, 21 May 2021 12:18:45 +0200 Subject: [PATCH 007/202] remove redundant passenv --- tox.ini | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tox.ini b/tox.ini index 4ad01238d..1097d7e6a 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,6 @@ envlist = pypy3 minversion = 3.14.2 requires = - # https://github.com/tox-dev/tox/issues/765 virtualenv >= 20.4.5 pip >= 21.1.1 @@ -29,29 +28,18 @@ skip_install = true commands = python -m pre_commit run {posargs:--all} deps = pre-commit>=1.20.0 -skip_install = true usedevelop = false [testenv:docs] -passenv = - LC_ALL - LANG - HOME commands = pip install -e . pip install -r test_requirements.txt pip install -r docs/requirements.txt make docs whitelist_externals = make -deps = -rtest_requirements.txt -skip_install = true [testenv:safety] -passenv = - LC_ALL - LANG - HOME commands = safety check --full-report deps = - safety \ No newline at end of file + safety From 823050a031c2b1d0e41f9feced3dc1baa715a425 Mon Sep 17 00:00:00 2001 From: Fabio Todaro Date: Mon, 31 May 2021 11:31:57 +0200 Subject: [PATCH 008/202] Run drafter only on master releases --- .github/release-drafter.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 6691c2c63..6eb8aeb13 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,3 +1,5 @@ +filter-by-commitish: true +commitish: master categories: - title: 'Breaking Changes' labels: From f00bed7b93aff1a7684bfb4dbba287999920b76f Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 14 Jun 2021 13:36:44 +0100 Subject: [PATCH 009/202] Adopt setuptools-scm packaging --- .github/workflows/main.yml | 2 + pyproject.toml | 12 ++++++ setup.cfg | 76 ++++++++++++++++++++++++++++++++++- setup.py | 81 ++++++-------------------------------- tox.ini | 25 ++++++++++++ 5 files changed, 126 insertions(+), 70 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 73c66aff7..14e8e77d9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,6 +24,8 @@ jobs: pip install tox virtualenv - name: Lint run: "tox -e lint" + - name: Packaging + run: "tox -e packaging" - name: Safety run: "tox -e safety" build: diff --git a/pyproject.toml b/pyproject.toml index 9c25cb239..6dc08afa9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,17 @@ +[build-system] +requires = [ + "setuptools >= 42.0.0", # required by pyproject+setuptools_scm integration + "setuptools_scm[toml] >= 3.5.0", # required for "no-local-version" scheme + "setuptools_scm_git_archive >= 1.0", + "wheel", +] +build-backend = "setuptools.build_meta" + [tool.black] skip-string-normalization = true exclude = '/(tests/hooks-abort-render/hooks|docs\/HelloCookieCutter1)/' line-length = 88 target-version = ['py36'] + +[tool.setuptools_scm] +local_scheme = "no-local-version" diff --git a/setup.cfg b/setup.cfg index aaf136bcb..609c2dc11 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,77 @@ +[metadata] +name = cookiecutter +url = https://github.com/cookiecutter/cookiecutter +project_urls = + Bug Tracker = https://github.com/cookiecutter/cookiecutter/issues + CI: GitHub = https://github.com/cookiecutter/cookiecutter/actions + Documentation = https://cookiecutter.readthedocs.io/ + Source Code = https://github.com/cookiecutter/cookiecutter +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 = file: README.md +long_description_content_type = text/markdown +author = Audrey Feldroy +author_email = audreyr@gmail.com +maintainer = Audrey Feldroy +maintainer_email = audreyr@gmail.com +license = BSD +license_file = LICENSE +classifiers = + Development Status :: 5 - Production/Stable + Environment :: Console + Intended Audience :: Developers + Natural Language :: English + License :: OSI Approved :: BSD License + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + Programming Language :: Python + Topic :: Software Development +keywords = + cookiecutter + Python + projects + project templates + Jinja2 + skeleton + scaffolding + project directory + package + packaging + +[options] +use_scm_version = True +python_requires = >=3.6 +; package_dir = +; = src +packages = cookiecutter +zip_safe = False + +# These are required during `setup.py` run: +setup_requires = + setuptools_scm>=1.15.0 + setuptools_scm_git_archive>=1.0 + +install_requires = + binaryornot>=0.4.4 + Jinja2>=2.7,<4.0.0 + click>=7.0,<8.0.0 + pyyaml>=5.3.1 + jinja2-time>=0.2.0 + python-slugify>=4.0.0 + requests>=2.23.0 + +[options.entry_points] +console_scripts = + cookiecutter = cookiecutter.__main__:main + [flake8] ignore = BLK100,E231,W503 @@ -11,7 +85,7 @@ statistics = 1 max-line-length = 88 [bdist_wheel] -universal = 1 +universal = false [tool:pytest] testpaths = tests diff --git a/setup.py b/setup.py index 654010fa0..5a2d616e8 100644 --- a/setup.py +++ b/setup.py @@ -1,70 +1,13 @@ -#!/usr/bin/env python -"""cookiecutter distutils configuration.""" -from setuptools import setup - -version = "2.0.0" - -with open('README.md', encoding='utf-8') as readme_file: - readme = readme_file.read() - -requirements = [ - 'binaryornot>=0.4.4', - 'Jinja2>=2.7,<4.0.0', - 'click>=7.0,<8.0.0', - 'pyyaml>=5.3.1', - 'jinja2-time>=0.2.0', - 'python-slugify>=4.0.0', - 'requests>=2.23.0', -] - -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=readme, - long_description_content_type='text/markdown', - author='Audrey Feldroy', - author_email='audreyr@gmail.com', - url='https://github.com/cookiecutter/cookiecutter', - packages=['cookiecutter'], - package_dir={'cookiecutter': 'cookiecutter'}, - entry_points={'console_scripts': ['cookiecutter = cookiecutter.__main__:main']}, - include_package_data=True, - python_requires='>=3.6', - install_requires=requirements, - 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 :: 3 :: Only", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Programming Language :: Python", - "Topic :: Software Development", - ], - keywords=[ - "cookiecutter", - "Python", - "projects", - "project templates", - "Jinja2", - "skeleton", - "scaffolding", - "project directory", - "package", - "packaging", - ], +#! /usr/bin/env python3 +"""cookiecutter distutils configuration. + +The presence of this file ensures the support +of pip editable mode *with setuptools only*. +""" +import setuptools + +# https://github.com/jazzband/pip-tools/issues/1278 +setuptools.setup( + use_scm_version={"local_scheme": "no-local-version"}, + setup_requires=["setuptools_scm[toml]>=3.5.0"], ) diff --git a/tox.ini b/tox.ini index 1097d7e6a..147c4d022 100644 --- a/tox.ini +++ b/tox.ini @@ -43,3 +43,28 @@ commands = safety check --full-report deps = safety + +[testenv:packaging] +description = + Build package, verify metadata, install package and assert behavior when ansible is missing. +deps = + build + twine +skip_install = true +commands = + {envpython} -c 'import os.path, shutil, sys; \ + dist_dir = os.path.join("{toxinidir}", "dist"); \ + os.path.isdir(dist_dir) or sys.exit(0); \ + print("Removing \{!s\} contents...".format(dist_dir), file=sys.stderr); \ + shutil.rmtree(dist_dir)' + # build using moder python build (PEP-517) + {envpython} -m build \ + --sdist \ + --wheel \ + --outdir {toxinidir}/dist/ \ + {toxinidir} + # Validate metadata using twine + twine check {toxinidir}/dist/* + # Install the wheel + sh -c "python3 -m pip install {toxinidir}/dist/*.whl" +whitelist_externals = sh From a54de6759d2553564cda1911fb553e9df9cad937 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Tue, 15 Jun 2021 12:17:49 -0400 Subject: [PATCH 010/202] Add missing period (#1574) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e1e6673a..9fcf0c7c5 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ We are proud to be an open source sponsor of Did someone say features? * Cross-platform: Windows, Mac, and Linux are officially supported. -* You don't have to know/write Python code to use Cookiecutter +* You don't have to know/write Python code to use Cookiecutter. * Works with Python 3.6, 3.7, 3.8, 3.9 and PyPy3. * Project templates can be in any programming language or markup format: Python, JavaScript, Ruby, CoffeeScript, RST, Markdown, CSS, HTML, you name it. From b1f6427606b67362de233588dd7a37496195b031 Mon Sep 17 00:00:00 2001 From: Claudio Jolowicz Date: Tue, 15 Jun 2021 19:31:55 +0200 Subject: [PATCH 011/202] Add support for click 8.x (#1569) * Make read_user_dict compatible with click 8.x The `read_user_dict` function uses a "default" sentinel instead of the actual default value. Being a JSON dict, the latter would often be hard to type. Under click 8.x, the default value for `click.prompt` is passed to the `read_proc` callback. We use this callback to load JSON from the user input, and this would choke on an input like "default" (without quotes). Therefore, change the callback to return the default value when it receives the "default" sentinel. Under click 7.x (which is our minimum version), the default value for `click.prompt` is returned as-is. Therefore, continue to handle the case where `click.prompt` returns "default" instead of the actual default value, but only if we're actually running under click 7.x. (Checking for click 7.x is only done for clarity. Under click 8.x, `click.prompt` would never return "default", even if a user entered it as a valid JSON string. This is because our callback requires a dict, not a string.) * test: Expect read_user_dict to call click.prompt with partial object Previously, tests for `read_user_dict` expected `process_json` to be passed to click.prompt directly. Instead, we now pass an instance of `functools.partial`, so adapt the mock to reflect that. * test: Avoid mocking `click.prompt` when testing defaults Do not mock `click.prompt` when testing that `read_user_dict` returns the proper default value (rather than the sentinel "default"). Mocking `click.prompt` prevents our callback from running, and under click >= 8.0 we process the sentinel in the callback. Instead, use `click.testing.CliRunner` to fake standard input. * test: Adapt regression test for default handling Expect `json.loads` not to be called with the sentinel ("default"). Previously, the test expected `process_json` not to be called, but under click >= 8.0 that is now where we handle the sentinel. * Update dependencies for click 8.x --- cookiecutter/prompt.py | 23 ++++++++++++++++------- setup.cfg | 2 +- tests/test_read_user_dict.py | 34 +++++++++++++++++----------------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index dfb8f3218..4b8b2fbe6 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -1,4 +1,5 @@ """Functions for prompting the user for project info.""" +import functools import json from collections import OrderedDict @@ -78,11 +79,18 @@ def read_user_choice(var_name, options): return choice_map[user_choice] -def process_json(user_value): +DEFAULT_DISPLAY = 'default' + + +def process_json(user_value, default_value=None): """Load user-supplied value as a JSON dict. :param str user_value: User-supplied value to load as a JSON dict """ + if user_value == DEFAULT_DISPLAY: + # Return the given default w/o any processing + return default_value + try: user_dict = json.loads(user_value, object_pairs_hook=OrderedDict) except Exception: @@ -107,15 +115,16 @@ def read_user_dict(var_name, default_value): if not isinstance(default_value, dict): raise TypeError - default_display = 'default' - user_value = click.prompt( - var_name, default=default_display, type=click.STRING, value_proc=process_json + var_name, + default=DEFAULT_DISPLAY, + type=click.STRING, + value_proc=functools.partial(process_json, default_value=default_value), ) - if user_value == default_display: - # Return the given default w/o any processing - return default_value + if click.__version__.startswith("7.") and user_value == DEFAULT_DISPLAY: + # click 7.x does not invoke value_proc on the default value. + return default_value # pragma: no cover return user_value diff --git a/setup.cfg b/setup.cfg index 609c2dc11..b9f2cf7a8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -62,7 +62,7 @@ setup_requires = install_requires = binaryornot>=0.4.4 Jinja2>=2.7,<4.0.0 - click>=7.0,<8.0.0 + click>=7.0,<9.0.0 pyyaml>=5.3.1 jinja2-time>=0.2.0 python-slugify>=4.0.0 diff --git a/tests/test_read_user_dict.py b/tests/test_read_user_dict.py index ccf632258..0ce50efc2 100644 --- a/tests/test_read_user_dict.py +++ b/tests/test_read_user_dict.py @@ -92,35 +92,35 @@ def test_should_call_prompt_with_process_json(mocker): read_user_dict('name', {'project_slug': 'pytest-plugin'}) - assert mock_prompt.call_args == mocker.call( - 'name', type=click.STRING, default='default', value_proc=process_json, - ) + args, kwargs = mock_prompt.call_args + + assert args == ('name',) + assert kwargs['type'] == click.STRING + assert kwargs['default'] == 'default' + assert kwargs['value_proc'].func == process_json -def test_should_not_call_process_json_default_value(mocker, monkeypatch): - """Make sure that `process_json` is not called when using default value.""" - mock_process_json = mocker.patch('cookiecutter.prompt.process_json', autospec=True) +def test_should_not_load_json_from_sentinel(mocker): + """Make sure that `json.loads` is not called when using default value.""" + mock_json_loads = mocker.patch( + 'cookiecutter.prompt.json.loads', autospec=True, return_value={} + ) runner = click.testing.CliRunner() with runner.isolation(input="\n"): read_user_dict('name', {'project_slug': 'pytest-plugin'}) - mock_process_json.assert_not_called() + mock_json_loads.assert_not_called() -def test_read_user_dict_default_value(mocker): +@pytest.mark.parametrize("input", ["\n", "default\n"]) +def test_read_user_dict_default_value(mocker, input): """Make sure that `read_user_dict` returns the default value. Verify return of a dict variable rather than the display value. """ - mock_prompt = mocker.patch( - 'cookiecutter.prompt.click.prompt', autospec=True, return_value='default', - ) - - val = read_user_dict('name', {'project_slug': 'pytest-plugin'}) - - assert mock_prompt.call_args == mocker.call( - 'name', type=click.STRING, default='default', value_proc=process_json, - ) + runner = click.testing.CliRunner() + with runner.isolation(input=input): + val = read_user_dict('name', {'project_slug': 'pytest-plugin'}) assert val == {'project_slug': 'pytest-plugin'} From d6037b7dee5756e35a6ecd5b522899a9061c2c79 Mon Sep 17 00:00:00 2001 From: Simone Basso <4202614+simobasso@users.noreply.github.com> Date: Tue, 15 Jun 2021 19:46:42 +0200 Subject: [PATCH 012/202] Enable branch coverage (#1542) * Add test cases for untested branches * Add branches coverage Branch coverage helps to find out untested branches. Closes: https://github.com/cookiecutter/cookiecutter/issues/1541 --- cookiecutter/vcs.py | 2 +- tests/test_utils.py | 10 +++++++ tests/vcs/test_clone.py | 22 +++++++++++++++ tests/zipfile/test_unzip.py | 56 +++++++++++++++++++++++++++++++++++++ tox.ini | 2 +- 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py index 746dfade9..d7f7b5ba5 100644 --- a/cookiecutter/vcs.py +++ b/cookiecutter/vcs.py @@ -81,7 +81,7 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): if repo_type == 'git': repo_name = repo_name.split(':')[-1].rsplit('.git')[0] repo_dir = os.path.normpath(os.path.join(clone_to_dir, repo_name)) - elif repo_type == 'hg': + if repo_type == 'hg': repo_dir = os.path.normpath(os.path.join(clone_to_dir, repo_name)) logger.debug('repo_dir is {0}'.format(repo_dir)) diff --git a/tests/test_utils.py b/tests/test_utils.py index 5b089ae4a..54d07b424 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -92,6 +92,16 @@ def test_work_in(tmp_path): assert cwd == Path.cwd() +def test_work_in_without_path(): + """Folder is not changed if no path provided.""" + cwd = Path.cwd() + + with utils.work_in(): + assert cwd == Path.cwd() + + assert cwd == Path.cwd() + + def test_prompt_should_ask_and_rm_repo_dir(mocker, tmp_path): """In `prompt_and_delete()`, if the user agrees to delete/reclone the \ repo, the repo should be deleted.""" diff --git a/tests/vcs/test_clone.py b/tests/vcs/test_clone.py index 9e3b78496..4687ea9a2 100644 --- a/tests/vcs/test_clone.py +++ b/tests/vcs/test_clone.py @@ -58,6 +58,28 @@ def test_clone_should_abort_if_user_does_not_want_to_reclone(mocker, clone_dir): assert not mock_subprocess.called +def test_clone_should_silent_exit_if_ok_to_reuse(mocker, tmpdir): + """In `clone()`, if user doesn't want to reclone, Cookiecutter should exit \ + without cloning anything.""" + mocker.patch('cookiecutter.vcs.is_vcs_installed', autospec=True, return_value=True) + mocker.patch( + 'cookiecutter.vcs.prompt_and_delete', return_value=False, autospec=True + ) + mock_subprocess = mocker.patch( + 'cookiecutter.vcs.subprocess.check_output', autospec=True, + ) + + clone_to_dir = tmpdir.mkdir('clone') + + # Create repo_dir to trigger prompt_and_delete + clone_to_dir.mkdir('cookiecutter-pytest-plugin') + + repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git' + + vcs.clone(repo_url, clone_to_dir=str(clone_to_dir)) + assert not mock_subprocess.called + + @pytest.mark.parametrize( 'repo_type, repo_url, repo_name', [ diff --git a/tests/zipfile/test_unzip.py b/tests/zipfile/test_unzip.py index 5135f4eec..0231d2b2a 100644 --- a/tests/zipfile/test_unzip.py +++ b/tests/zipfile/test_unzip.py @@ -2,6 +2,7 @@ import tempfile import pytest +import shutil from cookiecutter import zipfile from cookiecutter.exceptions import InvalidZipRepository @@ -16,6 +17,16 @@ def mock_download(): chunk = zf.read(1024) +def mock_download_with_empty_chunks(): + """Fake download function.""" + yield + with open('tests/files/fake-repo-tmpl.zip', 'rb') as zf: + chunk = zf.read(1024) + while chunk: + yield chunk + chunk = zf.read(1024) + + def test_unzip_local_file(mocker, clone_dir): """Local file reference can be unzipped.""" mock_prompt_and_delete = mocker.patch( @@ -170,6 +181,29 @@ def test_unzip_url(mocker, clone_dir): assert not mock_prompt_and_delete.called +def test_unzip_url_with_empty_chunks(mocker, clone_dir): + """In `unzip()` empty chunk must be ignored.""" + mock_prompt_and_delete = mocker.patch( + 'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True + ) + + request = mocker.MagicMock() + request.iter_content.return_value = mock_download_with_empty_chunks() + + mocker.patch( + 'cookiecutter.zipfile.requests.get', return_value=request, autospec=True, + ) + + output_dir = zipfile.unzip( + 'https://example.com/path/to/fake-repo-tmpl.zip', + is_url=True, + clone_to_dir=str(clone_dir), + ) + + assert output_dir.startswith(tempfile.gettempdir()) + assert not mock_prompt_and_delete.called + + def test_unzip_url_existing_cache(mocker, clone_dir): """Url should be downloaded and unzipped, old zip file will be removed.""" mock_prompt_and_delete = mocker.patch( @@ -240,3 +274,25 @@ def test_unzip_should_abort_if_no_redownload(mocker, clone_dir): zipfile.unzip(zipfile_url, is_url=True, clone_to_dir=str(clone_dir)) assert not mock_requests_get.called + + +def test_unzip_is_ok_to_reuse(mocker, clone_dir): + """Already downloaded zip should not be downloaded again.""" + mock_prompt_and_delete = mocker.patch( + 'cookiecutter.zipfile.prompt_and_delete', return_value=False, autospec=True + ) + + request = mocker.MagicMock() + + existing_zip = clone_dir.joinpath('fake-repo-tmpl.zip') + shutil.copy('tests/files/fake-repo-tmpl.zip', existing_zip) + + output_dir = zipfile.unzip( + 'https://example.com/path/to/fake-repo-tmpl.zip', + is_url=True, + clone_to_dir=str(clone_dir), + ) + + assert output_dir.startswith(tempfile.gettempdir()) + assert mock_prompt_and_delete.call_count == 1 + assert request.iter_content.call_count == 0 diff --git a/tox.ini b/tox.ini index 147c4d022..499c6febe 100644 --- a/tox.ini +++ b/tox.ini @@ -18,7 +18,7 @@ passenv = HOME commands = pip install -e . - pytest --cov=cookiecutter --cov-report=term --cov-fail-under=100 {posargs:tests} + pytest --cov=cookiecutter --cov-report=term --cov-fail-under=100 --cov-branch {posargs:tests} cov-report: coverage html cov-report: coverage xml deps = -rtest_requirements.txt From a46eede6fa45aedee0621ff1fe3e212dab17ee11 Mon Sep 17 00:00:00 2001 From: Jeremy Swerdlow Date: Thu, 5 Aug 2021 23:52:25 -0700 Subject: [PATCH 013/202] Expand cli documentation relating to the no-input flag (#1543) --- cookiecutter/cli.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py index 991e62c50..08d778b73 100644 --- a/cookiecutter/cli.py +++ b/cookiecutter/cli.py @@ -75,7 +75,9 @@ def list_installed_templates(default_config, passed_config_file): @click.option( '--no-input', is_flag=True, - help='Do not prompt for parameters and only use cookiecutter.json file content', + help='Do not prompt for parameters and only use cookiecutter.json file content. ' + 'Defaults to deleting any cached resources and redownloading them. ' + 'Cannot be combined with the --replay flag.', ) @click.option( '-c', '--checkout', help='branch, tag or commit to checkout after git clone', @@ -91,7 +93,8 @@ def list_installed_templates(default_config, passed_config_file): @click.option( '--replay', is_flag=True, - help='Do not prompt for parameters and only use information entered previously', + help='Do not prompt for parameters and only use information entered previously. ' + 'Cannot be combined with the --no-input flag or with extra configuration passed.', ) @click.option( '--replay-file', From 831b365e1448bc219e7d0508ffdd794675710b4a Mon Sep 17 00:00:00 2001 From: David Salter Date: Thu, 14 Oct 2021 22:48:03 +0100 Subject: [PATCH 014/202] Move contributors and backers to credits section --- AUTHORS.md | 45 ++++++++++++++++++++++++ docs/BACKERS.md | 1 - docs/index.rst | 2 -- docs/sprint-contributors.rst | 66 ------------------------------------ 4 files changed, 45 insertions(+), 69 deletions(-) delete mode 120000 docs/BACKERS.md delete mode 100644 docs/sprint-contributors.rst diff --git a/AUTHORS.md b/AUTHORS.md index d991d54fa..173d02b2f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -170,3 +170,48 @@ * Tom Forbes ([@orf](https://github.com/orf)) * Xie Yanbo ([@xyb](https://github.com/xyb)) * Maxim Ivanov ([@ivanovmg](https://github.com/ivanovmg)) + +## Backers + +We would like to thank the following people for supporting us in our efforts to maintain and improve Cookiecutter: + +* Alex DeBrie +* Alexandre Y. Harano +* Bruno Alla +* Carol Willing +* Russell Keith-Magee + +## Sprint Contributors + +### PyCon 2016 Sprint + +The following people made contributions to the cookiecutter project +at the PyCon sprints in Portland, OR from June 2-5 2016. +Contributions include user testing, debugging, improving documentation, +reviewing issues, writing tutorials, creating and updating project +templates, and teaching each other. + +* Adam Chainz ([@adamchainz](https://github.com/adamchainz)) +* Andrew Ittner ([@tephyr](https://github.com/tephyr)) +* Audrey Roy Greenfeld ([@audreyr](https://github.com/audreyr)) +* Carol Willing ([@willingc](https://github.com/willingc)) +* Christopher Clarke ([@chrisdev](https://github.com/chrisdev)) +* Citlalli Murillo ([@citmusa](https://github.com/citmusa)) +* Daniel Roy Greenfeld ([@pydanny](https://github.com/pydanny)) +* Diane DeMers Chen ([@purplediane](https://github.com/purplediane)) +* Elaine Wong ([@elainewong`](https://github.com/elainewong)) +* Elias Dorneles ([@eliasdorneles](https://github.com/eliasdorneles)) +* Emily Cain ([@emcain](https://github.com/emcain)) +* John Roa ([@jhonjairoroa87](https://github.com/jhonjairoroa87)) +* Jonan Scheffler ([@1337807](https://github.com/1337807)) +* Phoebe Bauer ([@phoebebauer](https://github.com/phoebebauer)) +* Kartik Sundararajan ([@skarbot](https://github.com/skarbot)) +* Katia Lira ([@katialira](https://github.com/katialira)) +* Leonardo Jimenez ([@xpostudio4](https://github.com/xpostudio4)) +* Lindsay Slazakowski ([@lslaz1](https://github.com/lslaz1)) +* Meghan Heintz ([@dot2dotseurat](https://github.com/dot2dotseurat)) +* Raphael Pierzina ([@hackebrot](https://github.com/hackebrot)) +* Umair Ashraf ([@umrashrf](https://github.com/umrashrf)) +* Valdir Stumm Junior ([@stummjr](https://github.com/stummjr)) +* Vivian Guillen ([@viviangb](https://github.com/viviangb)) +* Zaro ([@zaro0508](https://github.com/zaro0508)) diff --git a/docs/BACKERS.md b/docs/BACKERS.md deleted file mode 120000 index 6e7e6e905..000000000 --- a/docs/BACKERS.md +++ /dev/null @@ -1 +0,0 @@ -../BACKERS.md \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 750db5093..15a953e63 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -42,10 +42,8 @@ Project Info CONTRIBUTING AUTHORS - sprint-contributors HISTORY case_studies - BACKERS CODE_OF_CONDUCT Index diff --git a/docs/sprint-contributors.rst b/docs/sprint-contributors.rst deleted file mode 100644 index 1a8419fb8..000000000 --- a/docs/sprint-contributors.rst +++ /dev/null @@ -1,66 +0,0 @@ -=================== -Sprint Contributors -=================== - -PyCon 2016 Sprint ------------------ - -The following people made contributions to the cookiecutter project -at the PyCon sprints in Portland, OR from June 2-5 2016. -Contributions include user testing, debugging, improving documentation, -reviewing issues, writing tutorials, creating and updating project -templates, and teaching each other. - -* Adam Chainz (`@adamchainz`_) -* Andrew Ittner (`@tephyr`_) -* Audrey Roy Greenfeld (`@audreyr`_) -* Carol Willing (`@willingc`_) -* Christopher Clarke (`@chrisdev`_) -* Citlalli Murillo (`@citmusa`_) -* Daniel Roy Greenfeld (`@pydanny`_) -* Diane DeMers Chen (`@purplediane`_) -* Elaine Wong (`@elainewong`_) -* Elias Dorneles (`@eliasdorneles`_) -* Emily Cain (`@emcain`_) -* John Roa (`@jhonjairoroa87`_) -* Jonan Scheffler (`@1337807`_) -* Phoebe Bauer (`@phoebebauer`_) -* Kartik Sundararajan (`@skarbot`_) -* Katia Lira (`@katialira`_) -* Leonardo Jimenez (`@xpostudio4`_) -* Lindsay Slazakowski (`@lslaz1`_) -* Meghan Heintz (`@dot2dotseurat`_) -* Raphael Pierzina (`@hackebrot`_) -* Umair Ashraf (`@umrashrf`_) -* Valdir Stumm Junior (`@stummjr`_) -* Vivian Guillen (`@viviangb`_) -* Zaro (`@zaro0508`_) - - - - -.. _`@1337807`: https://github.com/1337807 -.. _`@adamchainz`: https://github.com/adamchainz -.. _`@audreyr`: https://github.com/audreyr -.. _`@chrisdev`: https://github.com/chrisdev -.. _`@citmusa`: https://github.com/citmusa -.. _`@dot2dotseurat`: https://github.com/dot2dotseurat -.. _`@elainewong`: https://github.com/elainewong -.. _`@eliasdorneles`: https://github.com/eliasdorneles -.. _`@emcain`: https://github.com/emcain -.. _`@hackebrot`: https://github.com/hackebrot -.. _`@jhonjairoroa87`: https://github.com/jhonjairoroa87 -.. _`@katialira`: https://github.com/katialira -.. _`@lslaz1`: https://github.com/lslaz1 -.. _`@phoebebauer`: https://github.com/phoebebauer -.. _`@purplediane`: https://github.com/purplediane -.. _`@pydanny`: https://github.com/pydanny -.. _`@skarbot`: https://github.com/skarbot -.. _`@stummjr`: https://github.com/stummjr -.. _`@tephyr`: https://github.com/tephyr -.. _`@umrashrf`: https://github.com/umrashrf -.. _`@viviangb`: https://github.com/viviangb -.. _`@willingc`: https://github.com/willingc -.. _`@xpostudio4`: https://github.com/xpostudio4 -.. _`@zaro0508`: https://github.com/zaro0508 - From e45e525fbc193001831cc936e23c7fa4927f7ad4 Mon Sep 17 00:00:00 2001 From: Giuseppe Lumia Date: Tue, 26 Oct 2021 22:20:36 +0200 Subject: [PATCH 015/202] Improve docstrings of the `no_input` flag Relates to #1543 --- cookiecutter/main.py | 4 +++- cookiecutter/prompt.py | 5 ++--- cookiecutter/repository.py | 3 ++- cookiecutter/vcs.py | 3 ++- cookiecutter/zipfile.py | 3 ++- docs/advanced/suppressing_prompts.rst | 2 ++ 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/cookiecutter/main.py b/cookiecutter/main.py index 047d30a08..ce47fa752 100644 --- a/cookiecutter/main.py +++ b/cookiecutter/main.py @@ -39,7 +39,9 @@ def cookiecutter( :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 no_input: Do not prompt for user input. + Use default values for template parameters taken from `cookiecutter.json`, user + config and `extra_dict`. Force a refresh of cached resources. :param extra_context: A dictionary of context that overrides default and user configuration. :param replay: Do not prompt for input, instead read from saved json. If diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index 4b8b2fbe6..c3aa6b957 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -168,10 +168,9 @@ def render_variable(env, raw, cookiecutter_dict): def prompt_choice_for_config(cookiecutter_dict, env, key, options, no_input): """Prompt user with a set of options to choose from. - Each of the possible choices is rendered beforehand. + :no_input: Do not prompt for user input and return the first available option. """ 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) @@ -181,7 +180,7 @@ def prompt_for_config(context, no_input=False): """Prompt user to enter a new config. :param dict context: Source for field names and sample values. - :param no_input: Prompt the user at command line for manual configuration? + :param no_input: Do not prompt for user input and use only values from context. """ cookiecutter_dict = OrderedDict([]) env = StrictEnvironment(context=context) diff --git a/cookiecutter/repository.py b/cookiecutter/repository.py index f8e6fcbcc..5086e9b01 100644 --- a/cookiecutter/repository.py +++ b/cookiecutter/repository.py @@ -82,7 +82,8 @@ def determine_repo_dir( definitions. :param clone_to_dir: The directory to clone the repository into. :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 no_input: Do not prompt for user input and eventually force a refresh of + cached resources. :param password: The password to use when extracting the repository. :param directory: Directory within repo where cookiecutter.json lives. :return: A tuple containing the cookiecutter template directory, and diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py index d7f7b5ba5..8b0b7744b 100644 --- a/cookiecutter/vcs.py +++ b/cookiecutter/vcs.py @@ -61,7 +61,8 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): :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. + :param no_input: Do not prompt for user input and eventually force a refresh of + cached resources. :returns: str with path to the new directory of the repository. """ # Ensure that clone_to_dir exists diff --git a/cookiecutter/zipfile.py b/cookiecutter/zipfile.py index 24925c7fc..620839b9e 100644 --- a/cookiecutter/zipfile.py +++ b/cookiecutter/zipfile.py @@ -20,7 +20,8 @@ def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None): :param is_url: Is the zip URI a URL or a file? :param clone_to_dir: The cookiecutter repository directory to put the archive into. - :param no_input: Suppress any prompts + :param no_input: Do not prompt for user input and eventually force a refresh of + cached resources. :param password: The password to use when unpacking the repository. """ # Ensure that clone_to_dir exists diff --git a/docs/advanced/suppressing_prompts.rst b/docs/advanced/suppressing_prompts.rst index 1f73ff9fd..d9608dd63 100644 --- a/docs/advanced/suppressing_prompts.rst +++ b/docs/advanced/suppressing_prompts.rst @@ -5,6 +5,8 @@ Suppressing Command-Line Prompts To suppress the prompts asking for input, use `no_input`. +Note: this option will force a refresh of cached resources. + Basic Example: Using the Defaults ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 8383971fbb4298e773233b6a6a00b4c25acfe4f4 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Sat, 30 Oct 2021 09:25:12 -0700 Subject: [PATCH 016/202] Ignore OSX-generated files --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index e646a076d..8c96562ec 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,8 @@ target/ # PyEnv .python-version + +# OSX +.DS_Store +.AppleDouble +.LSOverride From 53b3f14bd5481cdfab779688cf02029d4e6c013f Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Sat, 30 Oct 2021 10:27:31 -0700 Subject: [PATCH 017/202] Markdown corrections: - Name corrections for @audreyfeldroy and @pydanny - Run Prettier on Markdown files --- AUTHORS.md | 323 ++++++++++++++++++++++++++--------------------------- LICENSE | 2 +- README.md | 184 +++++++++++++++--------------- setup.cfg | 4 +- 4 files changed, 256 insertions(+), 257 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index d991d54fa..2f951cdd0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -2,171 +2,170 @@ ## Development Leads -* Audrey Feldroy ([@audreyfeldroy](https://github.com/audreyfeldroy)) -* Daniel Feldroy ([@pydanny](https://github.com/pydanny)) -* Raphael Pierzina ([@hackebrot](https://github.com/hackebrot)) - +- Audrey Roy Greenfeld ([@audreyfeldroy](https://github.com/audreyfeldroy)) +- Daniel Roy Greenfeld ([@pydanny](https://github.com/pydanny)) +- Raphael Pierzina ([@hackebrot](https://github.com/hackebrot)) ## Core Committers -* Michael Joseph ([@michaeljoseph](https://github.com/michaeljoseph)) -* Paul Moore ([@pfmoore](https://github.com/pfmoore)) -* Andrey Shpak ([@insspb](https://github.com/insspb)) -* Sorin Sbarnea ([@ssbarnea](https://github.com/ssbarnea)) -* Fábio C. Barrionuevo da Luz ([@luzfcb](https://github.com/luzfcb)) -* Simone Basso ([@simobasso](https://github.com/simobasso)) +- Michael Joseph ([@michaeljoseph](https://github.com/michaeljoseph)) +- Paul Moore ([@pfmoore](https://github.com/pfmoore)) +- Andrey Shpak ([@insspb](https://github.com/insspb)) +- Sorin Sbarnea ([@ssbarnea](https://github.com/ssbarnea)) +- Fábio C. Barrionuevo da Luz ([@luzfcb](https://github.com/luzfcb)) +- Simone Basso ([@simobasso](https://github.com/simobasso)) ## Contributors -* Steven Loria ([@sloria](https://github.com/sloria)) -* Goran Peretin ([@gperetin](https://github.com/gperetin)) -* Hamish Downer ([@foobacca](https://github.com/foobacca)) -* Thomas Orozco ([@krallin](https://github.com/krallin)) -* Jindrich Smitka ([@s-m-i-t-a](https://github.com/s-m-i-t-a)) -* Benjamin Schwarze ([@benjixx](https://github.com/benjixx)) -* Raphi ([@raphigaziano](https://github.com/raphigaziano)) -* Thomas Chiroux ([@ThomasChiroux](https://github.com/ThomasChiroux)) -* Sergi Almacellas Abellana ([@pokoli](https://github.com/pokoli)) -* Alex Gaynor ([@alex](https://github.com/alex)) -* Rolo ([@rolo](https://github.com/rolo)) -* Pablo ([@oubiga](https://github.com/oubiga)) -* Bruno Rocha ([@rochacbruno](https://github.com/rochacbruno)) -* Alexander Artemenko ([@svetlyak40wt](https://github.com/svetlyak40wt)) -* Mahmoud Abdelkader ([@mahmoudimus](https://github.com/mahmoudimus)) -* Leonardo Borges Avelino ([@lborgav](https://github.com/lborgav)) -* Chris Trotman ([@solarnz](https://github.com/solarnz)) -* Rolf ([@relekang](https://github.com/relekang)) -* Noah Kantrowitz ([@coderanger](https://github.com/coderanger)) -* Vincent Bernat ([@vincentbernat](https://github.com/vincentbernat)) -* Germán Moya ([@pbacterio](https://github.com/pbacterio)) -* Ned Batchelder ([@nedbat](https://github.com/nedbat)) -* Dave Dash ([@davedash](https://github.com/davedash)) -* Johan Charpentier ([@cyberj](https://github.com/cyberj)) -* Éric Araujo ([@merwok](https://github.com/merwok)) -* saxix ([@saxix](https://github.com/saxix)) -* Tzu-ping Chung ([@uranusjr](https://github.com/uranusjr)) -* Caleb Hattingh ([@cjrh](https://github.com/cjrh)) -* Flavio Curella ([@fcurella](https://github.com/fcurella)) -* Adam Venturella ([@aventurella](https://github.com/aventurella)) -* Monty Taylor ([@emonty](https://github.com/emonty)) -* schacki ([@schacki](https://github.com/schacki)) -* Ryan Olson ([@ryanolson](https://github.com/ryanolson)) -* Trey Hunner ([@treyhunner](https://github.com/treyhunner)) -* Russell Keith-Magee ([@freakboy3742](https://github.com/freakboy3742)) -* Mishbah Razzaque ([@mishbahr](https://github.com/mishbahr)) -* Robin Andeer ([@robinandeer](https://github.com/robinandeer)) -* Rachel Sanders ([@trustrachel](https://github.com/trustrachel)) -* Rémy Hubscher ([@Natim](https://github.com/Natim)) -* Dino Petron3 ([@dinopetrone](https://github.com/dinopetrone)) -* Peter Inglesby ([@inglesp](https://github.com/inglesp)) -* Ramiro Batista da Luz ([@ramiroluz](https://github.com/ramiroluz)) -* Omer Katz ([@thedrow](https://github.com/thedrow)) -* lord63 ([@lord63](https://github.com/lord63)) -* Randy Syring ([@rsyring](https://github.com/rsyring)) -* Mark Jones ([@mark0978](https://github.com/mark0978)) -* Marc Abramowitz ([@msabramo](https://github.com/msabramo)) -* Lucian Ursu ([@LucianU](https://github.com/LucianU)) -* Osvaldo Santana Neto ([@osantana](https://github.com/osantana)) -* Matthias84 ([@Matthias84](https://github.com/Matthias84)) -* Simeon Visser ([@svisser](https://github.com/svisser)) -* Guruprasad ([@lgp171188](https://github.com/lgp171188)) -* Charles-Axel Dein ([@charlax](https://github.com/charlax)) -* Diego Garcia ([@drgarcia1986](https://github.com/drgarcia1986)) -* maiksensi ([@maiksensi](https://github.com/maiksensi)) -* Andrew Conti ([@agconti](https://github.com/agconti)) -* Valentin Lab ([@vaab](https://github.com/vaab)) -* Ilja Bauer ([@iljabauer](https://github.com/iljabauer)) -* Elias Dorneles ([@eliasdorneles](https://github.com/eliasdorneles)) -* Matias Saguir ([@mativs](https://github.com/mativs)) -* Johannes ([@johtso](https://github.com/johtso)) -* macrotim ([@macrotim](https://github.com/macrotim)) -* Will McGinnis ([@wdm0006](https://github.com/wdm0006)) -* Cédric Krier ([@cedk](https://github.com/cedk)) -* Tim Osborn ([@ptim](https://github.com/ptim)) -* Aaron Gallagher ([@habnabit](https://github.com/habnabit)) -* mozillazg ([@mozillazg](https://github.com/mozillazg)) -* Joachim Jablon ([@ewjoachim](https://github.com/ewjoachim)) -* Andrew Ittner ([@tephyr](https://github.com/tephyr)) -* Diane DeMers Chen ([@purplediane](https://github.com/purplediane)) -* zzzirk ([@zzzirk](https://github.com/zzzirk)) -* Carol Willing ([@willingc](https://github.com/willingc)) -* phoebebauer ([@phoebebauer](https://github.com/phoebebauer)) -* Adam Chainz ([@adamchainz](https://github.com/adamchainz)) -* Sulé ([@suledev](https://github.com/suledev)) -* Evan Palmer ([@palmerev](https://github.com/palmerev)) -* Bruce Eckel ([@BruceEckel](https://github.com/BruceEckel)) -* Robert Lyon ([@ivanlyon](https://github.com/ivanlyon)) -* Terry Bates ([@terryjbates](https://github.com/terryjbates)) -* Brett Cannon ([@brettcannon](https://github.com/brettcannon)) -* Michael Warkentin ([@mwarkentin](https://github.com/mwarkentin)) -* Bartłomiej Kurzeja ([@B3QL](https://github.com/B3QL)) -* Thomas O'Donnell ([@andytom](https://github.com/andytom)) -* Jeremy Carbaugh ([@jcarbaugh](https://github.com/jcarbaugh)) -* Nathan Cheung ([@cheungnj](https://github.com/cheungnj)) -* Abdó Roig-Maranges ([@aroig](https://github.com/aroig)) -* Steve Piercy ([@stevepiercy](https://github.com/stevepiercy)) -* Corey ([@coreysnyder04](https://github.com/coreysnyder04)) -* Dmitry Evstratov ([@devstrat](https://github.com/devstrat)) -* Eyal Levin ([@eyalev](https://github.com/eyalev)) -* mathagician ([@mathagician](https://github.com/mathagician)) -* Guillaume Gelin ([@ramnes](https://github.com/ramnes)) -* @delirious-lettuce ([@delirious-lettuce](https://github.com/delirious-lettuce)) -* Gasper Vozel ([@karantan](https://github.com/karantan)) -* Joshua Carp ([@jmcarp](https://github.com/jmcarp)) -* @meahow ([@meahow](https://github.com/meahow)) -* Andrea Grandi ([@andreagrandi](https://github.com/andreagrandi)) -* Issa Jubril ([@jubrilissa](https://github.com/jubrilissa)) -* Nytiennzo Madooray ([@Nythiennzo](https://github.com/Nythiennzo)) -* Erik Bachorski ([@dornheimer](https://github.com/dornheimer)) -* cclauss ([@cclauss](https://github.com/cclauss)) -* Andy Craze ([@accraze](https://github.com/accraze)) -* Anthony Sottile ([@asottile](https://github.com/asottile)) -* Jonathan Sick ([@jonathansick](https://github.com/jonathansick)) -* Hugo ([@hugovk](https://github.com/hugovk)) -* Min ho Kim ([@minho42](https://github.com/minho42)) -* Ryan Ly ([@rly](https://github.com/rly)) -* Akintola Rahmat ([@mihrab34](https://github.com/mihrab34)) -* Jai Ram Rideout ([@jairideout](https://github.com/jairideout)) -* Diego Carrasco Gubernatis ([@dacog](https://github.com/dacog)) -* Wagner Negrão ([@wagnernegrao](https://github.com/wagnernegrao)) -* Josh Barnes ([@jcb91](https://github.com/jcb91)) -* Nikita Sobolev ([@sobolevn](https://github.com/sobolevn)) -* Matt Stibbs ([@mattstibbs](https://github.com/mattstibbs)) -* MinchinWeb ([@MinchinWeb](https://github.com/MinchinWeb)) -* kishan ([@kishan](https://github.com/kishan3)) -* tonytheleg ([@tonytheleg](https://github.com/tonytheleg)) -* Roman Hartmann ([@RomHartmann](https://github.com/RomHartmann)) -* DSEnvel ([@DSEnvel](https://github.com/DSEnvel)) -* kishan ([@kishan](https://github.com/kishan3)) -* Bruno Alla ([@browniebroke](https://github.com/browniebroke)) -* nicain ([@nicain](https://github.com/nicain)) -* Carsten Rösnick-Neugebauer ([@croesnick](https://github.com/croesnick)) -* igorbasko01 ([@igorbasko01](https://github.com/igorbasko01)) -* Dan Booth Dev ([@DanBoothDev](https://github.com/DanBoothDev)) -* Pablo Panero ([@ppanero](https://github.com/ppanero)) -* Chuan-Heng Hsiao ([@chhsiao1981](https://github.com/chhsiao1981)) -* Mohammad Hossein Sekhavat ([@mhsekhavat](https://github.com/mhsekhavat)) -* Amey Joshi ([@amey589](https://github.com/amey589)) -* Paul Harrison ([@smoothml](https://github.com/smoothml)) -* Fabio Todaro ([@SharpEdgeMarshall](https://github.com/SharpEdgeMarshall)) -* Nicholas Bollweg ([@bollwyvl](https://github.com/bollwyvl)) -* Jace Browning ([@jacebrowning](https://github.com/jacebrowning)) -* Ionel Cristian Mărieș ([@ionelmc](https://github.com/ionelmc)) -* Kishan Mehta ([@kishan3](https://github.com/kishan3)) -* Wieland Hoffmann ([@mineo](https://github.com/mineo)) -* Antony Lee ([@anntzer](https://github.com/anntzer)) -* Aurélien Gâteau ([@agateau](https://github.com/agateau)) -* Axel H. ([@noirbizarre](https://github.com/noirbizarre)) -* Chris ([@chrisbrake](https://github.com/chrisbrake)) -* Chris Streeter ([@streeter](https://github.com/streeter)) -* Gábor Lipták ([@gliptak](https://github.com/gliptak)) -* Javier Sánchez Portero ([@javiersanp](https://github.com/javiersanp)) -* Nimrod Milo ([@milonimrod](https://github.com/milonimrod)) -* Philipp Kats ([@Casyfill](https://github.com/Casyfill)) -* Reinout van Rees ([@reinout](https://github.com/reinout)) -* Rémy Greinhofer ([@rgreinho](https://github.com/rgreinho)) -* Sebastian ([@sebix](https://github.com/sebix)) -* Stuart Mumford ([@Cadair](https://github.com/Cadair)) -* Tom Forbes ([@orf](https://github.com/orf)) -* Xie Yanbo ([@xyb](https://github.com/xyb)) -* Maxim Ivanov ([@ivanovmg](https://github.com/ivanovmg)) +- Steven Loria ([@sloria](https://github.com/sloria)) +- Goran Peretin ([@gperetin](https://github.com/gperetin)) +- Hamish Downer ([@foobacca](https://github.com/foobacca)) +- Thomas Orozco ([@krallin](https://github.com/krallin)) +- Jindrich Smitka ([@s-m-i-t-a](https://github.com/s-m-i-t-a)) +- Benjamin Schwarze ([@benjixx](https://github.com/benjixx)) +- Raphi ([@raphigaziano](https://github.com/raphigaziano)) +- Thomas Chiroux ([@ThomasChiroux](https://github.com/ThomasChiroux)) +- Sergi Almacellas Abellana ([@pokoli](https://github.com/pokoli)) +- Alex Gaynor ([@alex](https://github.com/alex)) +- Rolo ([@rolo](https://github.com/rolo)) +- Pablo ([@oubiga](https://github.com/oubiga)) +- Bruno Rocha ([@rochacbruno](https://github.com/rochacbruno)) +- Alexander Artemenko ([@svetlyak40wt](https://github.com/svetlyak40wt)) +- Mahmoud Abdelkader ([@mahmoudimus](https://github.com/mahmoudimus)) +- Leonardo Borges Avelino ([@lborgav](https://github.com/lborgav)) +- Chris Trotman ([@solarnz](https://github.com/solarnz)) +- Rolf ([@relekang](https://github.com/relekang)) +- Noah Kantrowitz ([@coderanger](https://github.com/coderanger)) +- Vincent Bernat ([@vincentbernat](https://github.com/vincentbernat)) +- Germán Moya ([@pbacterio](https://github.com/pbacterio)) +- Ned Batchelder ([@nedbat](https://github.com/nedbat)) +- Dave Dash ([@davedash](https://github.com/davedash)) +- Johan Charpentier ([@cyberj](https://github.com/cyberj)) +- Éric Araujo ([@merwok](https://github.com/merwok)) +- saxix ([@saxix](https://github.com/saxix)) +- Tzu-ping Chung ([@uranusjr](https://github.com/uranusjr)) +- Caleb Hattingh ([@cjrh](https://github.com/cjrh)) +- Flavio Curella ([@fcurella](https://github.com/fcurella)) +- Adam Venturella ([@aventurella](https://github.com/aventurella)) +- Monty Taylor ([@emonty](https://github.com/emonty)) +- schacki ([@schacki](https://github.com/schacki)) +- Ryan Olson ([@ryanolson](https://github.com/ryanolson)) +- Trey Hunner ([@treyhunner](https://github.com/treyhunner)) +- Russell Keith-Magee ([@freakboy3742](https://github.com/freakboy3742)) +- Mishbah Razzaque ([@mishbahr](https://github.com/mishbahr)) +- Robin Andeer ([@robinandeer](https://github.com/robinandeer)) +- Rachel Sanders ([@trustrachel](https://github.com/trustrachel)) +- Rémy Hubscher ([@Natim](https://github.com/Natim)) +- Dino Petron3 ([@dinopetrone](https://github.com/dinopetrone)) +- Peter Inglesby ([@inglesp](https://github.com/inglesp)) +- Ramiro Batista da Luz ([@ramiroluz](https://github.com/ramiroluz)) +- Omer Katz ([@thedrow](https://github.com/thedrow)) +- lord63 ([@lord63](https://github.com/lord63)) +- Randy Syring ([@rsyring](https://github.com/rsyring)) +- Mark Jones ([@mark0978](https://github.com/mark0978)) +- Marc Abramowitz ([@msabramo](https://github.com/msabramo)) +- Lucian Ursu ([@LucianU](https://github.com/LucianU)) +- Osvaldo Santana Neto ([@osantana](https://github.com/osantana)) +- Matthias84 ([@Matthias84](https://github.com/Matthias84)) +- Simeon Visser ([@svisser](https://github.com/svisser)) +- Guruprasad ([@lgp171188](https://github.com/lgp171188)) +- Charles-Axel Dein ([@charlax](https://github.com/charlax)) +- Diego Garcia ([@drgarcia1986](https://github.com/drgarcia1986)) +- maiksensi ([@maiksensi](https://github.com/maiksensi)) +- Andrew Conti ([@agconti](https://github.com/agconti)) +- Valentin Lab ([@vaab](https://github.com/vaab)) +- Ilja Bauer ([@iljabauer](https://github.com/iljabauer)) +- Elias Dorneles ([@eliasdorneles](https://github.com/eliasdorneles)) +- Matias Saguir ([@mativs](https://github.com/mativs)) +- Johannes ([@johtso](https://github.com/johtso)) +- macrotim ([@macrotim](https://github.com/macrotim)) +- Will McGinnis ([@wdm0006](https://github.com/wdm0006)) +- Cédric Krier ([@cedk](https://github.com/cedk)) +- Tim Osborn ([@ptim](https://github.com/ptim)) +- Aaron Gallagher ([@habnabit](https://github.com/habnabit)) +- mozillazg ([@mozillazg](https://github.com/mozillazg)) +- Joachim Jablon ([@ewjoachim](https://github.com/ewjoachim)) +- Andrew Ittner ([@tephyr](https://github.com/tephyr)) +- Diane DeMers Chen ([@purplediane](https://github.com/purplediane)) +- zzzirk ([@zzzirk](https://github.com/zzzirk)) +- Carol Willing ([@willingc](https://github.com/willingc)) +- phoebebauer ([@phoebebauer](https://github.com/phoebebauer)) +- Adam Chainz ([@adamchainz](https://github.com/adamchainz)) +- Sulé ([@suledev](https://github.com/suledev)) +- Evan Palmer ([@palmerev](https://github.com/palmerev)) +- Bruce Eckel ([@BruceEckel](https://github.com/BruceEckel)) +- Robert Lyon ([@ivanlyon](https://github.com/ivanlyon)) +- Terry Bates ([@terryjbates](https://github.com/terryjbates)) +- Brett Cannon ([@brettcannon](https://github.com/brettcannon)) +- Michael Warkentin ([@mwarkentin](https://github.com/mwarkentin)) +- Bartłomiej Kurzeja ([@B3QL](https://github.com/B3QL)) +- Thomas O'Donnell ([@andytom](https://github.com/andytom)) +- Jeremy Carbaugh ([@jcarbaugh](https://github.com/jcarbaugh)) +- Nathan Cheung ([@cheungnj](https://github.com/cheungnj)) +- Abdó Roig-Maranges ([@aroig](https://github.com/aroig)) +- Steve Piercy ([@stevepiercy](https://github.com/stevepiercy)) +- Corey ([@coreysnyder04](https://github.com/coreysnyder04)) +- Dmitry Evstratov ([@devstrat](https://github.com/devstrat)) +- Eyal Levin ([@eyalev](https://github.com/eyalev)) +- mathagician ([@mathagician](https://github.com/mathagician)) +- Guillaume Gelin ([@ramnes](https://github.com/ramnes)) +- @delirious-lettuce ([@delirious-lettuce](https://github.com/delirious-lettuce)) +- Gasper Vozel ([@karantan](https://github.com/karantan)) +- Joshua Carp ([@jmcarp](https://github.com/jmcarp)) +- @meahow ([@meahow](https://github.com/meahow)) +- Andrea Grandi ([@andreagrandi](https://github.com/andreagrandi)) +- Issa Jubril ([@jubrilissa](https://github.com/jubrilissa)) +- Nytiennzo Madooray ([@Nythiennzo](https://github.com/Nythiennzo)) +- Erik Bachorski ([@dornheimer](https://github.com/dornheimer)) +- cclauss ([@cclauss](https://github.com/cclauss)) +- Andy Craze ([@accraze](https://github.com/accraze)) +- Anthony Sottile ([@asottile](https://github.com/asottile)) +- Jonathan Sick ([@jonathansick](https://github.com/jonathansick)) +- Hugo ([@hugovk](https://github.com/hugovk)) +- Min ho Kim ([@minho42](https://github.com/minho42)) +- Ryan Ly ([@rly](https://github.com/rly)) +- Akintola Rahmat ([@mihrab34](https://github.com/mihrab34)) +- Jai Ram Rideout ([@jairideout](https://github.com/jairideout)) +- Diego Carrasco Gubernatis ([@dacog](https://github.com/dacog)) +- Wagner Negrão ([@wagnernegrao](https://github.com/wagnernegrao)) +- Josh Barnes ([@jcb91](https://github.com/jcb91)) +- Nikita Sobolev ([@sobolevn](https://github.com/sobolevn)) +- Matt Stibbs ([@mattstibbs](https://github.com/mattstibbs)) +- MinchinWeb ([@MinchinWeb](https://github.com/MinchinWeb)) +- kishan ([@kishan](https://github.com/kishan3)) +- tonytheleg ([@tonytheleg](https://github.com/tonytheleg)) +- Roman Hartmann ([@RomHartmann](https://github.com/RomHartmann)) +- DSEnvel ([@DSEnvel](https://github.com/DSEnvel)) +- kishan ([@kishan](https://github.com/kishan3)) +- Bruno Alla ([@browniebroke](https://github.com/browniebroke)) +- nicain ([@nicain](https://github.com/nicain)) +- Carsten Rösnick-Neugebauer ([@croesnick](https://github.com/croesnick)) +- igorbasko01 ([@igorbasko01](https://github.com/igorbasko01)) +- Dan Booth Dev ([@DanBoothDev](https://github.com/DanBoothDev)) +- Pablo Panero ([@ppanero](https://github.com/ppanero)) +- Chuan-Heng Hsiao ([@chhsiao1981](https://github.com/chhsiao1981)) +- Mohammad Hossein Sekhavat ([@mhsekhavat](https://github.com/mhsekhavat)) +- Amey Joshi ([@amey589](https://github.com/amey589)) +- Paul Harrison ([@smoothml](https://github.com/smoothml)) +- Fabio Todaro ([@SharpEdgeMarshall](https://github.com/SharpEdgeMarshall)) +- Nicholas Bollweg ([@bollwyvl](https://github.com/bollwyvl)) +- Jace Browning ([@jacebrowning](https://github.com/jacebrowning)) +- Ionel Cristian Mărieș ([@ionelmc](https://github.com/ionelmc)) +- Kishan Mehta ([@kishan3](https://github.com/kishan3)) +- Wieland Hoffmann ([@mineo](https://github.com/mineo)) +- Antony Lee ([@anntzer](https://github.com/anntzer)) +- Aurélien Gâteau ([@agateau](https://github.com/agateau)) +- Axel H. ([@noirbizarre](https://github.com/noirbizarre)) +- Chris ([@chrisbrake](https://github.com/chrisbrake)) +- Chris Streeter ([@streeter](https://github.com/streeter)) +- Gábor Lipták ([@gliptak](https://github.com/gliptak)) +- Javier Sánchez Portero ([@javiersanp](https://github.com/javiersanp)) +- Nimrod Milo ([@milonimrod](https://github.com/milonimrod)) +- Philipp Kats ([@Casyfill](https://github.com/Casyfill)) +- Reinout van Rees ([@reinout](https://github.com/reinout)) +- Rémy Greinhofer ([@rgreinho](https://github.com/rgreinho)) +- Sebastian ([@sebix](https://github.com/sebix)) +- Stuart Mumford ([@Cadair](https://github.com/Cadair)) +- Tom Forbes ([@orf](https://github.com/orf)) +- Xie Yanbo ([@xyb](https://github.com/xyb)) +- Maxim Ivanov ([@ivanovmg](https://github.com/ivanovmg)) diff --git a/LICENSE b/LICENSE index 5e75b2cfd..06486a8f3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2021, Audrey Feldroy +Copyright (c) 2013-2021, Audrey Roy Greenfeld All rights reserved. Redistribution and use in source and binary forms, with or diff --git a/README.md b/README.md index 9fcf0c7c5..2d8b81b75 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,10 @@ 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: [https://cookiecutter.readthedocs.io](https://cookiecutter.readthedocs.io) -* GitHub: [https://github.com/cookiecutter/cookiecutter](https://github.com/cookiecutter/cookiecutter) -* PyPI: [https://pypi.org/project/cookiecutter/](https://pypi.org/project/cookiecutter/) -* Free and open source software: [BSD license](https://github.com/cookiecutter/cookiecutter/blob/master/LICENSE) +- Documentation: [https://cookiecutter.readthedocs.io](https://cookiecutter.readthedocs.io) +- GitHub: [https://github.com/cookiecutter/cookiecutter](https://github.com/cookiecutter/cookiecutter) +- PyPI: [https://pypi.org/project/cookiecutter/](https://pypi.org/project/cookiecutter/) +- Free and open source software: [BSD license](https://github.com/cookiecutter/cookiecutter/blob/master/LICENSE) ![Cookiecutter](https://raw.githubusercontent.com/cookiecutter/cookiecutter/3ac078356adf5a1a72042dfe72ebfa4a9cd5ef38/logo/cookiecutter_medium.png) @@ -26,13 +26,13 @@ We are proud to be an open source sponsor of Did someone say features? -* Cross-platform: Windows, Mac, and Linux are officially supported. -* You don't have to know/write Python code to use Cookiecutter. -* Works with Python 3.6, 3.7, 3.8, 3.9 and PyPy3. -* Project templates can be in any programming language or markup format: +- Cross-platform: Windows, Mac, and Linux are officially supported. +- You don't have to know/write Python code to use Cookiecutter. +- Works with Python 3.6, 3.7, 3.8, 3.9 and PyPy3. +- 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: +- Simple command line usage: ```bash # Create project from the cookiecutter-pypackage.git repo template @@ -44,7 +44,7 @@ $ cookiecutter https://github.com/audreyfeldroy/cookiecutter-pypackage $ cookiecutter gh:audreyfeldroy/cookiecutter-pypackage ``` -* Use it at the command line with a local template: +- Use it at the command line with a local template: ```bash # Create project in the current working directory, from the local @@ -52,7 +52,7 @@ $ cookiecutter gh:audreyfeldroy/cookiecutter-pypackage $ cookiecutter cookiecutter-pypackage/ ``` -* Or use it from Python: +- Or use it from Python: ```py from cookiecutter.main import cookiecutter @@ -64,47 +64,47 @@ cookiecutter('cookiecutter-pypackage/') cookiecutter('https://github.com/audreyfeldroy/cookiecutter-pypackage.git') ``` -* Directory names and filenames can be templated. For example: +- Directory names and filenames can be templated. For example: ```py {{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: +- 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: ```json { - "full_name": "Audrey Feldroy", - "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" + "full_name": "Audrey Roy Greenfeld", + "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: +- 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: ```yaml default_context: - full_name: "Audrey Feldroy" - email: "audreyr@gmail.com" - github_username: "audreyfeldroy" + full_name: "Audrey Roy Greenfeld" + email: "audreyr@gmail.com" + github_username: "audreyfeldroy" cookiecutters_dir: "~/.cookiecutters/" ``` -* Cookiecutters (cloned Cookiecutter project templates) are put into -``~/.cookiecutters/`` by default, or cookiecutters_dir if specified. -* If you have already cloned a cookiecutter into ``~/.cookiecutters/``, -you can reference it by directory name: +- Cookiecutters (cloned Cookiecutter project templates) are put into + `~/.cookiecutters/` by default, or cookiecutters_dir if specified. +- If you have already cloned a cookiecutter into `~/.cookiecutters/`, + you can reference it by directory name: ```bash # Clone cookiecutter-pypackage @@ -113,22 +113,22 @@ $ cookiecutter gh:audreyfeldroy/cookiecutter-pypackage $ cookiecutter cookiecutter-pypackage ``` -* 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. -* Inject extra context with command-line arguments: +- 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. +- Inject extra context with command-line arguments: ```bash cookiecutter --no-input gh:msabramo/cookiecutter-supervisor program_name=foobar startsecs=10 ``` -* 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 generated to your current directory or to target directory if -specified with `-o` option. +- 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 generated to your current directory or to target directory if + specified with `-o` option. ## Available Cookiecutters @@ -157,14 +157,14 @@ discoverable. You are almost not limited in topics amount, use it! These Cookiecutters are maintained by the cookiecutter team: -* [cookiecutter-pypackage](https://github.com/audreyfeldroy/cookiecutter-pypackage): -[@audreyfeldroy's](https://github.com/audreyfeldroy) ultimate Python package project template. -* [cookiecutter-django](https://github.com/pydanny/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-pytest-plugin](https://github.com/pytest-dev/cookiecutter-pytest-plugin): -Minimal Cookiecutter template for authoring [pytest](https://docs.pytest.org/) -plugins that help you to write better programs. +- [cookiecutter-pypackage](https://github.com/audreyfeldroy/cookiecutter-pypackage): + [@audreyfeldroy's](https://github.com/audreyfeldroy) ultimate Python package project template. +- [cookiecutter-django](https://github.com/pydanny/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-pytest-plugin](https://github.com/pytest-dev/cookiecutter-pytest-plugin): + Minimal Cookiecutter template for authoring [pytest](https://docs.pytest.org/) + plugins that help you to write better programs. ## Community @@ -173,57 +173,57 @@ We are always welcome and invite you to participate. Stuck? Try one of the following: -* See the [Troubleshooting](https://cookiecutter.readthedocs.io/en/latest/troubleshooting.html) page. -* Ask for help on [Stack Overflow](https://stackoverflow.com/questions/tagged/cookiecutter). -* You are strongly encouraged to -[file an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) -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 [Slack](https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U) -if you must (but please try one of the other options first, so that others -can benefit from the discussion). +- See the [Troubleshooting](https://cookiecutter.readthedocs.io/en/latest/troubleshooting.html) page. +- Ask for help on [Stack Overflow](https://stackoverflow.com/questions/tagged/cookiecutter). +- You are strongly encouraged to + [file an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) + 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 [Slack](https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U) + 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](AUTHORS.md) who have pitched in to help -make Cookiecutter an even better tool. -* Everyone is invited to contribute. Read the -[contributing instructions](CONTRIBUTING.md), then get started. -* Connect with other Cookiecutter contributors and users on -[Slack](https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U) -(note: due to work and commitments, a core committer might not always be available) +- Huge thanks to all the [contributors](AUTHORS.md) who have pitched in to help + make Cookiecutter an even better tool. +- Everyone is invited to contribute. Read the + [contributing instructions](CONTRIBUTING.md), then get started. +- Connect with other Cookiecutter contributors and users on + [Slack](https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U) + (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](https://github.com/cookiecutter/cookiecutter). -* [Support this project](#support-this-project) +- Thank a core committer for their efforts. +- Star [Cookiecutter on GitHub](https://github.com/cookiecutter/cookiecutter). +- [Support this project](#support-this-project) Got criticism or complaints? -* [File an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) -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](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) -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." +- [File an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) + 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](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) + 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? -[@audreyfeldroy](https://github.com/audreyfeldroy) is available for hire for consultation -or custom development. +- 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? + [@audreyfeldroy](https://github.com/audreyfeldroy) is available for hire for consultation + or custom development. ## Support This Project diff --git a/setup.cfg b/setup.cfg index b9f2cf7a8..c43971e88 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,9 +12,9 @@ description = Python package project template. long_description = file: README.md long_description_content_type = text/markdown -author = Audrey Feldroy +author = Audrey Roy Greenfeld author_email = audreyr@gmail.com -maintainer = Audrey Feldroy +maintainer = Audrey Roy Greenfeld maintainer_email = audreyr@gmail.com license = BSD license_file = LICENSE From 228c655550a57822d44b0c5527d5aab5986f266a Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Sat, 30 Oct 2021 10:54:22 -0700 Subject: [PATCH 018/202] Don't run tests on Python 3.6 or PyPy anymore. - Resolves GitHub check failures due to GitHub no longer supporting those environments on Mac OSX. - Python 3.6 is in maintenance status *security* with EOL in 2 months. - PyPy was last updated to Python 3.8, which is outdated. --- .github/workflows/main.yml | 25 ------------------------- CONTRIBUTING.md | 6 +++--- README.md | 4 +--- docs/installation.rst | 2 +- setup.cfg | 2 -- tox.ini | 2 -- 6 files changed, 5 insertions(+), 36 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 14e8e77d9..436de42a6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,28 +34,19 @@ jobs: fail-fast: false matrix: name: - - "ubuntu-py36" - "ubuntu-py37" - "ubuntu-py38" - "ubuntu-py39" - - "ubuntu-pypy3" - - "macos-py36" - "macos-py37" - "macos-py38" - "macos-py39" - - "macos-pypy3" - - "windows-py36" - "windows-py37" - "windows-py38" - "windows-py39" include: - - name: "ubuntu-py36" - python: "3.6" - os: ubuntu-latest - tox_env: "py36" - name: "ubuntu-py37" python: "3.7" os: ubuntu-latest @@ -68,15 +59,7 @@ jobs: python: "3.9" os: ubuntu-latest tox_env: "py39" - - name: "ubuntu-pypy3" - python: "pypy3" - os: ubuntu-latest - tox_env: "pypy3" - - name: "macos-py36" - python: "3.6" - os: macos-latest - tox_env: "py36" - name: "macos-py37" python: "3.7" os: macos-latest @@ -89,15 +72,7 @@ jobs: python: "3.9" os: macos-latest tox_env: "py39" - - name: "macos-pypy3" - python: "pypy3" - os: macos-latest - tox_env: "pypy3" - - name: "windows-py36" - python: "3.6" - os: windows-latest - tox_env: "py36" - name: "windows-py37" python: "3.7" os: windows-latest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4b9a5dff4..9c2f12650 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -188,13 +188,13 @@ 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: +It is possible to test with specific versions of Python. To do this, the command is: ```bash -tox -e py36,pypy3 +tox -e py37,py38 ``` -Will run py.test with the python3.6 and pypy3 interpreters, for example. +Will run py.test with the python3.7 and python3.8 interpreters, for example. ## Core Committer Guide diff --git a/README.md b/README.md index 2d8b81b75..9b1dfe141 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,9 @@ We are proud to be an open source sponsor of ## Features -Did someone say features? - - Cross-platform: Windows, Mac, and Linux are officially supported. - You don't have to know/write Python code to use Cookiecutter. -- Works with Python 3.6, 3.7, 3.8, 3.9 and PyPy3. +- Works with Python 3.7, 3.8, 3.9. - 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. diff --git a/docs/installation.rst b/docs/installation.rst index 44c0d841a..d12709c37 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -59,7 +59,7 @@ You may also install `Windows Subsystem for Linux =3.6. See the Python Packaging Authority's (PyPA) documentation `Requirements for Installing Packages `_ for full details. +See the Python Packaging Authority's (PyPA) documentation `Requirements for Installing Packages `_ for full details. Install cookiecutter diff --git a/setup.cfg b/setup.cfg index c43971e88..e1ddc4fbb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,12 +26,10 @@ classifiers = License :: OSI Approved :: BSD License Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: Implementation :: CPython - Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python Topic :: Software Development keywords = diff --git a/tox.ini b/tox.ini index 499c6febe..591fc50ee 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,9 @@ [tox] envlist = lint - py36 py37 py38 py39 - pypy3 minversion = 3.14.2 requires = virtualenv >= 20.4.5 From dfebae46c2922185615cb4b9e5aa24274ab7b4b9 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Sat, 30 Oct 2021 10:57:26 -0700 Subject: [PATCH 019/202] Update Black target version from py36 to py39 Co-authored-by: Daniel Roy Greenfeld --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6dc08afa9..26bea3f21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ build-backend = "setuptools.build_meta" skip-string-normalization = true exclude = '/(tests/hooks-abort-render/hooks|docs\/HelloCookieCutter1)/' line-length = 88 -target-version = ['py36'] +target-version = ['py39'] [tool.setuptools_scm] local_scheme = "no-local-version" From 120c8fa2794a244a8ea3de6ecc3994e2a7e3ba13 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Sat, 30 Oct 2021 11:09:24 -0700 Subject: [PATCH 020/202] Correct linting - Update Black version - Use Black python target of Python 3.9 Co-authored-by: Daniel Roy Greenfeld --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c1f2a0886..f183b3f18 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,8 +10,8 @@ repos: language: python files: \.rst$ require_serial: true - - repo: https://github.com/python/black.git - rev: 19.10b0 + - repo: https://github.com/psf/black.git + rev: 21.9b0 hooks: - id: black language_version: python3 From 7189ca0e1438bb755401b5012e1de7a8137373f5 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Wed, 3 Nov 2021 20:27:10 -0700 Subject: [PATCH 021/202] Forced re-run of PR CI --- .pre-commit-config.yaml | 2 +- CONTRIBUTING.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f183b3f18..8b6b8eb0f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: hooks: - id: doc8 name: doc8 - description: This hook runs doc8 for linting docs + description: This hook runs doc8 for linting docs. entry: python -m doc8 language: python files: \.rst$ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9c2f12650..0d484ed03 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,7 +72,7 @@ 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: ```bash -mkvirtualenv cookiecutter + cd cookiecutter/ python setup.py develop ``` From 81f0195252bfe72fcef3d0037874ce763337bc60 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Wed, 3 Nov 2021 20:36:23 -0700 Subject: [PATCH 022/202] Comment out doc8 to see if it is the issue with GHCI --- .pre-commit-config.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8b6b8eb0f..229ec0f18 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,15 @@ --- repos: - - repo: https://github.com/PyCQA/doc8 - rev: 0.8.1 - hooks: - - id: doc8 - name: doc8 - description: This hook runs doc8 for linting docs. - entry: python -m doc8 - language: python - files: \.rst$ - require_serial: true + # - repo: https://github.com/PyCQA/doc8 + # rev: 0.8.1 + # hooks: + # - id: doc8 + # name: doc8 + # description: This hook runs doc8 for linting docs. + # entry: python -m doc8 + # language: python + # files: \.rst$ + # require_serial: true - repo: https://github.com/psf/black.git rev: 21.9b0 hooks: From 5987775cfd54c07ba8ba8e3794bb69fda1242f16 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Wed, 3 Nov 2021 20:39:06 -0700 Subject: [PATCH 023/202] Test to see if Black is failing --- .pre-commit-config.yaml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 229ec0f18..ba22afc71 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,21 +1,21 @@ --- repos: - # - repo: https://github.com/PyCQA/doc8 - # rev: 0.8.1 - # hooks: - # - id: doc8 - # name: doc8 - # description: This hook runs doc8 for linting docs. - # entry: python -m doc8 - # language: python - # files: \.rst$ - # require_serial: true - - repo: https://github.com/psf/black.git - rev: 21.9b0 + - repo: https://github.com/PyCQA/doc8 + rev: 0.8.1 hooks: - - id: black - language_version: python3 - exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) + - id: doc8 + name: doc8 + description: This hook runs doc8 for linting docs. + entry: python -m doc8 + language: python + files: \.rst$ + require_serial: true + # - repo: https://github.com/psf/black.git + # rev: 21.9b0 + # hooks: + # - id: black + # language_version: python3 + # exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 hooks: From c767bd91786c884774dabb5686660c6f2b26d51f Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Wed, 3 Nov 2021 20:43:56 -0700 Subject: [PATCH 024/202] Add no-verify agument to pre-commit hooks --- .pre-commit-config.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba22afc71..246050ae4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,17 +10,17 @@ repos: language: python files: \.rst$ require_serial: true - # - repo: https://github.com/psf/black.git - # rev: 21.9b0 - # hooks: - # - id: black - # language_version: python3 - # exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) + - repo: https://github.com/psf/black.git + rev: 21.9b0 + hooks: + - id: black + language_version: python3 + exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 hooks: - id: trailing-whitespace - args: [--markdown-linebreak-ext=md] + args: [--markdown-linebreak-ext=md, --no-verify] - id: mixed-line-ending - id: check-byte-order-marker - id: check-executables-have-shebangs From def2bb64de18621db88d3529654f856cbbe7456a Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Wed, 3 Nov 2021 20:45:52 -0700 Subject: [PATCH 025/202] Upgrade black --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 246050ae4..ce7d43cfc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: files: \.rst$ require_serial: true - repo: https://github.com/psf/black.git - rev: 21.9b0 + rev: 21.10b0 hooks: - id: black language_version: python3 @@ -20,7 +20,7 @@ repos: rev: v2.4.0 hooks: - id: trailing-whitespace - args: [--markdown-linebreak-ext=md, --no-verify] + args: [--markdown-linebreak-ext=md] - id: mixed-line-ending - id: check-byte-order-marker - id: check-executables-have-shebangs From 3b5a4340c6178cfe7d9652520736eba680092cfc Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Wed, 3 Nov 2021 20:47:44 -0700 Subject: [PATCH 026/202] Comment out black so the PR can proceed --- .pre-commit-config.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ce7d43cfc..d4f0cf881 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,12 +10,12 @@ repos: language: python files: \.rst$ require_serial: true - - repo: https://github.com/psf/black.git - rev: 21.10b0 - hooks: - - id: black - language_version: python3 - exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) + # - repo: https://github.com/psf/black.git + # rev: 21.10b0 + # hooks: + # - id: black + # language_version: python3 + # exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 hooks: From c9e36a42382959a4b1ce796d2379bf83646df764 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Fri, 5 Nov 2021 10:55:01 -0700 Subject: [PATCH 027/202] Run pre-commit linting - Run tox twice, which lints the code base via pre-commit --- .pre-commit-config.yaml | 12 +++++----- cookiecutter/cli.py | 4 +++- cookiecutter/main.py | 3 ++- tests/test_cli.py | 42 +++++++++++++++++++++++++++------- tests/test_generate_context.py | 8 ++++++- tests/test_get_config.py | 6 ++++- tests/test_get_user_config.py | 6 ++++- tests/test_main.py | 23 ++++++++++++++----- tests/test_prompt.py | 3 ++- tests/test_utils.py | 3 ++- tests/vcs/test_clone.py | 12 ++++++---- tests/zipfile/test_unzip.py | 19 +++++++++++---- 12 files changed, 105 insertions(+), 36 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d4f0cf881..ce7d43cfc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,12 +10,12 @@ repos: language: python files: \.rst$ require_serial: true - # - repo: https://github.com/psf/black.git - # rev: 21.10b0 - # hooks: - # - id: black - # language_version: python3 - # exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) + - repo: https://github.com/psf/black.git + rev: 21.10b0 + hooks: + - id: black + language_version: python3 + exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 hooks: diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py index 991e62c50..84241ac2a 100644 --- a/cookiecutter/cli.py +++ b/cookiecutter/cli.py @@ -78,7 +78,9 @@ def list_installed_templates(default_config, passed_config_file): help='Do not prompt for parameters and only use cookiecutter.json file content', ) @click.option( - '-c', '--checkout', help='branch, tag or commit to checkout after git clone', + '-c', + '--checkout', + help='branch, tag or commit to checkout after git clone', ) @click.option( '--directory', diff --git a/cookiecutter/main.py b/cookiecutter/main.py index 047d30a08..7c9eef731 100644 --- a/cookiecutter/main.py +++ b/cookiecutter/main.py @@ -60,7 +60,8 @@ def cookiecutter( raise InvalidModeException(err_msg) config_dict = get_user_config( - config_file=config_file, default_config=default_config, + config_file=config_file, + default_config=default_config, ) repo_dir, cleanup = determine_repo_dir( diff --git a/tests/test_cli.py b/tests/test_cli.py index e357fef2e..623945e8b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -311,7 +311,10 @@ def test_default_user_config_overwrite(mocker, cli_runner, user_config_path): template_path = 'tests/fake-repo-pre/' result = cli_runner( - template_path, '--config-file', user_config_path, '--default-config', + template_path, + '--config-file', + user_config_path, + '--default-config', ) assert result.exit_code == 0 @@ -362,7 +365,11 @@ def test_echo_undefined_variable_error(output_dir, cli_runner): template_path = 'tests/undefined-variable/file-name/' result = cli_runner( - '--no-input', '--default-config', '--output-dir', output_dir, template_path, + '--no-input', + '--default-config', + '--output-dir', + output_dir, + template_path, ) assert result.exit_code == 1 @@ -392,7 +399,11 @@ def test_echo_unknown_extension_error(output_dir, cli_runner): template_path = 'tests/test-extensions/unknown/' result = cli_runner( - '--no-input', '--default-config', '--output-dir', output_dir, template_path, + '--no-input', + '--default-config', + '--output-dir', + output_dir, + template_path, ) assert result.exit_code == 1 @@ -404,7 +415,10 @@ def test_echo_unknown_extension_error(output_dir, cli_runner): def test_cli_extra_context(cli_runner): """Cli invocation replace content if called with replacement pairs.""" result = cli_runner( - 'tests/fake-repo-pre/', '--no-input', '-v', 'project_name=Awesomez', + 'tests/fake-repo-pre/', + '--no-input', + '-v', + 'project_name=Awesomez', ) assert result.exit_code == 0 assert os.path.isdir('fake-project') @@ -416,7 +430,10 @@ def test_cli_extra_context(cli_runner): def test_cli_extra_context_invalid_format(cli_runner): """Cli invocation raise error if called with unknown argument.""" result = cli_runner( - 'tests/fake-repo-pre/', '--no-input', '-v', 'ExtraContextWithNoEqualsSoInvalid', + 'tests/fake-repo-pre/', + '--no-input', + '-v', + 'ExtraContextWithNoEqualsSoInvalid', ) assert result.exit_code == 2 assert "Error: Invalid value for '[EXTRA_CONTEXT]...'" in result.output @@ -438,7 +455,10 @@ def test_debug_file_non_verbose(cli_runner, debug_file): assert not debug_file.exists() result = cli_runner( - '--no-input', '--debug-file', str(debug_file), 'tests/fake-repo-pre/', + '--no-input', + '--debug-file', + str(debug_file), + 'tests/fake-repo-pre/', ) assert result.exit_code == 0 @@ -493,7 +513,10 @@ def test_debug_list_installed_templates(cli_runner, debug_file, user_config_path open(os.path.join('fake-project', 'cookiecutter.json'), 'w').write('{}') result = cli_runner( - '--list-installed', '--config-file', user_config_path, str(debug_file), + '--list-installed', + '--config-file', + user_config_path, + str(debug_file), ) assert "1 installed templates:" in result.output @@ -520,7 +543,10 @@ def test_debug_list_installed_templates_failure( def test_directory_repo(cli_runner): """Test cli invocation works with `directory` option.""" result = cli_runner( - 'tests/fake-repo-dir/', '--no-input', '-v', '--directory=my-dir', + 'tests/fake-repo-dir/', + '--no-input', + '-v', + '--directory=my-dir', ) assert result.exit_code == 0 assert os.path.isdir("fake-project") diff --git a/tests/test_generate_context.py b/tests/test_generate_context.py index 2a2bc06dd..44ab99194 100644 --- a/tests/test_generate_context.py +++ b/tests/test_generate_context.py @@ -110,7 +110,13 @@ def test_default_context_replacement_in_generate_context(): def test_generate_context_decodes_non_ascii_chars(): """Verify `generate_context` correctly decodes non-ascii chars.""" - expected_context = {'non_ascii': OrderedDict([('full_name', 'éèà'),])} + expected_context = { + 'non_ascii': OrderedDict( + [ + ('full_name', 'éèà'), + ] + ) + } generated_context = generate.generate_context( context_file='tests/test-generate-context/non_ascii.json' diff --git a/tests/test_get_config.py b/tests/test_get_config.py index 760db3cdb..a37317413 100644 --- a/tests/test_get_config.py +++ b/tests/test_get_config.py @@ -62,7 +62,11 @@ def test_get_config(): 'github_username': 'example', 'project': { 'description': 'description', - 'tags': ['first', 'second', 'third',], + 'tags': [ + 'first', + 'second', + 'third', + ], }, }, 'abbreviations': { diff --git a/tests/test_get_user_config.py b/tests/test_get_user_config.py index 560c1d873..551502c5c 100644 --- a/tests/test_get_user_config.py +++ b/tests/test_get_user_config.py @@ -48,7 +48,11 @@ def custom_config(): 'github_username': 'example', 'project': { 'description': 'description', - 'tags': ['first', 'second', 'third',], + 'tags': [ + 'first', + 'second', + 'third', + ], }, }, 'cookiecutters_dir': '/home/example/some-path-to-templates', diff --git a/tests/test_main.py b/tests/test_main.py index 46b7ff9a8..ee0f738f9 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -22,11 +22,16 @@ def test_replay_dump_template_name( mocker.patch('cookiecutter.main.generate_files') cookiecutter( - '.', no_input=True, replay=False, config_file=user_config_file, + '.', + no_input=True, + replay=False, + config_file=user_config_file, ) mock_replay_dump.assert_called_once_with( - user_config_data['replay_dir'], 'fake-repo-tmpl', mocker.ANY, + user_config_data['replay_dir'], + 'fake-repo-tmpl', + mocker.ANY, ) @@ -46,11 +51,14 @@ def test_replay_load_template_name( mocker.patch('cookiecutter.main.generate_files') cookiecutter( - '.', replay=True, config_file=user_config_file, + '.', + replay=True, + config_file=user_config_file, ) mock_replay_load.assert_called_once_with( - user_config_data['replay_dir'], 'fake-repo-tmpl', + user_config_data['replay_dir'], + 'fake-repo-tmpl', ) @@ -62,9 +70,12 @@ def test_custom_replay_file(monkeypatch, mocker, user_config_file): mocker.patch('cookiecutter.main.generate_files') cookiecutter( - '.', replay='./custom-replay-file', config_file=user_config_file, + '.', + replay='./custom-replay-file', + config_file=user_config_file, ) mock_replay_load.assert_called_once_with( - '.', 'custom-replay-file', + '.', + 'custom-replay-file', ) diff --git a/tests/test_prompt.py b/tests/test_prompt.py index 0932ab573..8187cb283 100644 --- a/tests/test_prompt.py +++ b/tests/test_prompt.py @@ -80,7 +80,8 @@ class TestPrompt(object): def test_prompt_for_config(self, monkeypatch, context): """Verify `prompt_for_config` call `read_user_variable` on text request.""" monkeypatch.setattr( - 'cookiecutter.prompt.read_user_variable', lambda var, default: default, + 'cookiecutter.prompt.read_user_variable', + lambda var, default: default, ) cookiecutter_dict = prompt.prompt_for_config(context) diff --git a/tests/test_utils.py b/tests/test_utils.py index 54d07b424..988fc3887 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -122,7 +122,8 @@ def test_prompt_should_ask_and_exit_on_user_no_answer(mocker, tmp_path): """In `prompt_and_delete()`, if the user decline to delete/reclone the \ repo, cookiecutter should exit.""" mock_read_user = mocker.patch( - 'cookiecutter.utils.read_user_yes_no', return_value=False, + 'cookiecutter.utils.read_user_yes_no', + return_value=False, ) mock_sys_exit = mocker.patch('sys.exit', return_value=True) repo_dir = Path(tmp_path, 'repo') diff --git a/tests/vcs/test_clone.py b/tests/vcs/test_clone.py index 4687ea9a2..cd4ac13d7 100644 --- a/tests/vcs/test_clone.py +++ b/tests/vcs/test_clone.py @@ -24,7 +24,8 @@ def test_clone_should_rstrip_trailing_slash_in_repo_url(mocker, clone_dir): mocker.patch('cookiecutter.vcs.is_vcs_installed', autospec=True, return_value=True) mock_subprocess = mocker.patch( - 'cookiecutter.vcs.subprocess.check_output', autospec=True, + 'cookiecutter.vcs.subprocess.check_output', + autospec=True, ) vcs.clone('https://github.com/foo/bar/', clone_to_dir=str(clone_dir), no_input=True) @@ -44,7 +45,8 @@ def test_clone_should_abort_if_user_does_not_want_to_reclone(mocker, clone_dir): 'cookiecutter.vcs.prompt_and_delete', side_effect=SystemExit, autospec=True ) mock_subprocess = mocker.patch( - 'cookiecutter.vcs.subprocess.check_output', autospec=True, + 'cookiecutter.vcs.subprocess.check_output', + autospec=True, ) # Create repo_dir to trigger prompt_and_delete @@ -66,7 +68,8 @@ def test_clone_should_silent_exit_if_ok_to_reuse(mocker, tmpdir): 'cookiecutter.vcs.prompt_and_delete', return_value=False, autospec=True ) mock_subprocess = mocker.patch( - 'cookiecutter.vcs.subprocess.check_output', autospec=True, + 'cookiecutter.vcs.subprocess.check_output', + autospec=True, ) clone_to_dir = tmpdir.mkdir('clone') @@ -103,7 +106,8 @@ def test_clone_should_invoke_vcs_command( mocker.patch('cookiecutter.vcs.is_vcs_installed', autospec=True, return_value=True) mock_subprocess = mocker.patch( - 'cookiecutter.vcs.subprocess.check_output', autospec=True, + 'cookiecutter.vcs.subprocess.check_output', + autospec=True, ) expected_repo_dir = os.path.normpath(os.path.join(clone_dir, repo_name)) diff --git a/tests/zipfile/test_unzip.py b/tests/zipfile/test_unzip.py index 0231d2b2a..2bf58b00f 100644 --- a/tests/zipfile/test_unzip.py +++ b/tests/zipfile/test_unzip.py @@ -168,7 +168,9 @@ def test_unzip_url(mocker, clone_dir): request.iter_content.return_value = mock_download() mocker.patch( - 'cookiecutter.zipfile.requests.get', return_value=request, autospec=True, + 'cookiecutter.zipfile.requests.get', + return_value=request, + autospec=True, ) output_dir = zipfile.unzip( @@ -191,7 +193,9 @@ def test_unzip_url_with_empty_chunks(mocker, clone_dir): request.iter_content.return_value = mock_download_with_empty_chunks() mocker.patch( - 'cookiecutter.zipfile.requests.get', return_value=request, autospec=True, + 'cookiecutter.zipfile.requests.get', + return_value=request, + autospec=True, ) output_dir = zipfile.unzip( @@ -214,7 +218,9 @@ def test_unzip_url_existing_cache(mocker, clone_dir): request.iter_content.return_value = mock_download() mocker.patch( - 'cookiecutter.zipfile.requests.get', return_value=request, autospec=True, + 'cookiecutter.zipfile.requests.get', + return_value=request, + autospec=True, ) # Create an existing cache of the zipfile @@ -237,7 +243,9 @@ def test_unzip_url_existing_cache_no_input(mocker, clone_dir): request.iter_content.return_value = mock_download() mocker.patch( - 'cookiecutter.zipfile.requests.get', return_value=request, autospec=True, + 'cookiecutter.zipfile.requests.get', + return_value=request, + autospec=True, ) # Create an existing cache of the zipfile @@ -261,7 +269,8 @@ def test_unzip_should_abort_if_no_redownload(mocker, clone_dir): ) mock_requests_get = mocker.patch( - 'cookiecutter.zipfile.requests.get', autospec=True, + 'cookiecutter.zipfile.requests.get', + autospec=True, ) # Create an existing cache of the zipfile From 3db109d9815a21088429ce24d3ec982c4ae8f32f Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Wed, 10 Nov 2021 09:08:03 -0800 Subject: [PATCH 028/202] Update CONTRIBUTING.md Co-authored-by: Michael Joseph --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d484ed03..c17a916c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -194,7 +194,7 @@ It is possible to test with specific versions of Python. To do this, the command tox -e py37,py38 ``` -Will run py.test with the python3.7 and python3.8 interpreters, for example. +This will run `py.test` with the `python3.7` and `python3.8` interpreters. ## Core Committer Guide From 8de299ebe5e97ecd909a059d3fcd2bc8ce862bdc Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Sat, 11 Dec 2021 22:00:06 +0530 Subject: [PATCH 029/202] Get python version from version_info tuple Co-authored-by: Sahil --- cookiecutter/cli.py | 2 +- tests/test_cli.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py index 84241ac2a..6ce8f0ada 100644 --- a/cookiecutter/cli.py +++ b/cookiecutter/cli.py @@ -25,7 +25,7 @@ def version_msg(): """Return the Cookiecutter version, location and Python powering it.""" - python_version = sys.version[:3] + python_version = str(sys.version_info[0]) + '.' + str(sys.version_info[1]) location = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) message = 'Cookiecutter %(version)s from {} (Python {})' return message.format(location, python_version) diff --git a/tests/test_cli.py b/tests/test_cli.py index 623945e8b..b5c8d3c78 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -53,6 +53,8 @@ def test_cli_version(cli_runner, version_cli_flag): assert result.exit_code == 0 assert result.output.startswith('Cookiecutter') +def test_version_msg(): + print("to do") @pytest.mark.usefixtures('make_fake_project_dir', 'remove_fake_project_dir') def test_cli_error_on_existing_output_directory(cli_runner): From 3fa6b0e5071ea337950c1df6867073e6c45f9e06 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Sat, 11 Dec 2021 10:43:58 -0800 Subject: [PATCH 030/202] Update HISTORY.md --- HISTORY.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 132a1e028..0760c1d2b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,10 +2,96 @@ History is important, but our current roadmap can be found [here](https://github.com/cookiecutter/cookiecutter/projects) -## 1.8.0 (Current master, in development) - -* Do not modify this file, since 1.7.1 Changes are generated on Pull request -title and will be added before release. +## 2.0.1 (2021-12-11) +### Breaking Changes + +* Release preparation for 2.0.1rc1 (#1608) @audreyfeldroy +* Replace poyo with pyyaml. (#1489) @dHannasch +* Added: Path templates will be rendered when copy_without_render used (#839) @noirbizarre +* Added: End of line detection and configuration. (#1407) @insspb +* Remove support for python2.7 (#1386) @ssbarnea + +### Minor Changes + +* Adopt setuptools-scm packaging (#1577) @ssbarnea +* Log the error message when git clone fails, not just the return code (#1505) @logworthy +* allow jinja 3.0.0 (#1548) @wouterdb +* Added uuid extension to be able to generate uuids (#1493) @jonaswre +* Alert user if choice is invalid (#1496) @dHannasch +* Replace poyo with pyyaml. (#1489) @dHannasch +* update AUTHOR lead (#1532) @HosamAlmoghraby +* Add Python 3.9 (#1478) @gliptak +* Added: --list-installed cli option, listing already downloaded cookiecutter packages (#1096) @chrisbrake +* Added: Jinja2 Environment extension on files generation stage (#1419) @insspb +* Added: --replay-file cli option, for replay file distributing (#906) @Cadair +* Added: _output_dir to cookiecutter context (#1034) @Casyfill +* Added: CLI option to ignore hooks (#992) @rgreinho +* Changed: Generated projects can use multiple type hooks at same time. (sh + py) (#974) @milonimrod +* Added: Path templates will be rendered when copy_without_render used (#839) @noirbizarre +* Added: End of line detection and configuration. (#1407) @insspb +* Making code python 3 only: Remove python2 u' sign, fix some strings (#1402) @insspb +* py3: remove futures, six and encoding (#1401) @insspb +* Render variables starting with an underscore. (#1339) @smoothml +* Tests refactoring: test_utils write issues fixed #1405 (#1406) @insspb + +### CI/CD and QA changes + +* enable branch coverage (#1542) @simobasso +* Make release-drafter diff only between master releases (#1568) @SharpEdgeMarshall +* ensure filesystem isolation during tests execution (#1564) @simobasso +* add safety ci step (#1560) @simobasso +* pre-commit: add bandit hook (#1559) @simobasso +* Replace tmpdir in favour of tmp_path (#1545) @SharpEdgeMarshall +* Fix linting in CI (#1546) @SharpEdgeMarshall +* Coverage 100% (#1526) @SharpEdgeMarshall +* Run coverage with matrix (#1521) @SharpEdgeMarshall +* Lint rst files (#1443) @ssbarnea +* Python3: Changed io.open to build-in open (PEP3116) (#1408) @insspb +* Making code python 3 only: Remove python2 u' sign, fix some strings (#1402) @insspb +* py3: remove futures, six and encoding (#1401) @insspb +* Removed: Bumpversion, setup.py arguments. (#1404) @insspb +* Tests refactoring: test_utils write issues fixed #1405 (#1406) @insspb +* Added: Automatic PyPI deploy on tag creation (#1400) @insspb +* Changed: Restored coverage reporter (#1399) @insspb + +### Documentation updates + +* Fix pull requests checklist reference (#1537) @glumia +* Fix author name (#1544) @HosamAlmoghraby +* Add missing contributors (#1535) @glumia +* Update CONTRIBUTING.md (#1529) @glumia +* Update LICENSE (#1519) @simobasso +* docs: rewrite the conditional files / directories example description. (#1437) @lyz-code +* Fix incorrect years in release history (#1473) @graue70 +* Add slugify in the default extensions list (#1470) @oncleben31 +* Renamed cookiecutter.package to API (#1442) @grrlic +* Fixed wording detail (#1427) @steltenpower +* Changed: CLI Commands documentation engine (#1418) @insspb +* Added: Example for conditional files / directories in hooks (#1397) @xyb +* Changed: README.md PyPI URLs changed to the modern PyPI last version (#1391) @brettcannon +* Fixed: Comma in README.md (#1390) @Cy-dev-tex +* Fixed: Replaced no longer maintained pipsi by pipx (#1395) @ndclt + +### Bugfixes + +* Add support for click 8.x (#1569) @cjolowicz +* Force click<8.0.0 (#1562) @SharpEdgeMarshall +* Remove direct dependency on markupsafe (#1549) @ssbarnea +* fixes prompting private rendered dicts (#1504) @juhuebner +* User's JSON parse error causes ugly Python exception #809 (#1468) @noone234 +* config: set default on missing default_context key (#1516) @simobasso +* Fixed: Values encoding on Windows (#1414) @agateau +* Fixed: Fail with gitolite repositories (#1144) @javiersanp +* MANIFEST: Fix file name extensions (#1387) @sebix + +### Deprecations + +* Removed: Bumpversion, setup.py arguments. (#1404) @insspb +* Removed support for Python 3.6 and PyPy (#1608) @audreyfeldroy + +### This release was made possible by our wonderful contributors: + +@Cadair, @Casyfill, @Cy-dev-tex, @HosamAlmoghraby, @SharpEdgeMarshall, @agateau, @audreyfeldroy, @brettcannon, @chrisbrake, @cjolowicz, @dHannasch, @gliptak, @glumia, @graue70, @grrlic, @insspb, @javiersanp, @jonaswre, @jsoref, @Jthevos, @juhuebner, @logworthy, @lyz-code, @milonimrod, @ndclt, @noirbizarre, @noone234, @oncleben31, @ozer619, @rgreinho, @sebix, @Sahil-101, @simobasso, @smoothml, @ssbarnea, @steltenpower, @wouterdb, @xyb, Christopher Wolfe and Hosam Almoghraby ( RIAG Digital ) ## 1.7.2 (2020-04-21) From 4741a0575cbe75dcf618e88ef8c3e0e3b2225e66 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Sat, 11 Dec 2021 10:54:51 -0800 Subject: [PATCH 031/202] Bump version number to 2.0.1 Co-authored-by: Sahil Co-authored-by: Prathamesh Co-authored-by: John-Anthony --- cookiecutter/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookiecutter/__init__.py b/cookiecutter/__init__.py index 774de19bf..fd2954f2e 100644 --- a/cookiecutter/__init__.py +++ b/cookiecutter/__init__.py @@ -1,2 +1,2 @@ """Main package for Cookiecutter.""" -__version__ = "2.0.0" +__version__ = "2.0.1" From b1403ead3bcac644eb2619642dee4b325f7b8b08 Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Sun, 12 Dec 2021 13:18:30 +0530 Subject: [PATCH 032/202] Added test for version_msg Co-authored-by: Sahil --- .github/workflows/main.yml | 5 +++++ tests/test_cli.py | 22 ++++++++++++++++++++-- tox.ini | 1 + 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 436de42a6..b4e49ae73 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,6 +37,7 @@ jobs: - "ubuntu-py37" - "ubuntu-py38" - "ubuntu-py39" + - "ubuntu-py310" - "macos-py37" - "macos-py38" @@ -59,6 +60,10 @@ jobs: python: "3.9" os: ubuntu-latest tox_env: "py39" + - name: "ubuntu-py310" + python: "3.10" + os: ubuntu-latest + tox_env: "py310" - name: "macos-py37" python: "3.7" diff --git a/tests/test_cli.py b/tests/test_cli.py index b5c8d3c78..12ca5f5bb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,6 +4,9 @@ import os import re +import sys + + import pytest from click.testing import CliRunner @@ -53,8 +56,23 @@ def test_cli_version(cli_runner, version_cli_flag): assert result.exit_code == 0 assert result.output.startswith('Cookiecutter') -def test_version_msg(): - print("to do") + +def test_version_msg(cli_runner, version_cli_flag): + """Verify correct output for Cookiecutter Python version.""" + result = cli_runner(version_cli_flag) + python_major_number = sys.version_info.major + python_minor_number = sys.version_info.minor + version_output_string = result.output[:-1] + version_output_string = version_output_string[-5:-1] + if version_output_string[0] == ' ': + version_output_string = version_output_string[1:] + if version_output_string[-1] == ')': + version_output_string = version_output_string[:-1] + assert ( + str(python_major_number) + '.' + str(python_minor_number) + == version_output_string + ) + @pytest.mark.usefixtures('make_fake_project_dir', 'remove_fake_project_dir') def test_cli_error_on_existing_output_directory(cli_runner): diff --git a/tox.ini b/tox.ini index 591fc50ee..b8dd5c5e1 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,7 @@ envlist = py37 py38 py39 + py310 minversion = 3.14.2 requires = virtualenv >= 20.4.5 From e5e0e96a0212219e9b80210f85b464b6226c83bc Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Sun, 12 Dec 2021 22:21:56 +0530 Subject: [PATCH 033/202] Use f strings and add Python 3.10 CI environments Co-authored-by: Sahil --- .github/workflows/main.yml | 10 ++++++++++ cookiecutter/cli.py | 2 +- tests/test_cli.py | 13 ++++--------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b4e49ae73..12dce4e24 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,10 +42,12 @@ jobs: - "macos-py37" - "macos-py38" - "macos-py39" + - "macos-py310" - "windows-py37" - "windows-py38" - "windows-py39" + - "windows-py310" include: - name: "ubuntu-py37" @@ -77,6 +79,10 @@ jobs: python: "3.9" os: macos-latest tox_env: "py39" + - name: "macos-py310" + python: "3.10" + os: macos-latest + tox_env: "py310" - name: "windows-py37" python: "3.7" @@ -90,6 +96,10 @@ jobs: python: "3.9" os: windows-latest tox_env: "py39" + - name: "windows-py310" + python: "3.10" + os: windows-latest + tox_env: "py310" steps: - uses: actions/checkout@v2 diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py index 6ce8f0ada..385e024c5 100644 --- a/cookiecutter/cli.py +++ b/cookiecutter/cli.py @@ -25,7 +25,7 @@ def version_msg(): """Return the Cookiecutter version, location and Python powering it.""" - python_version = str(sys.version_info[0]) + '.' + str(sys.version_info[1]) + python_version = f'{sys.version_info.major}.{sys.version_info.minor}' location = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) message = 'Cookiecutter %(version)s from {} (Python {})' return message.format(location, python_version) diff --git a/tests/test_cli.py b/tests/test_cli.py index 12ca5f5bb..180314373 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -51,14 +51,14 @@ def version_cli_flag(request): def test_cli_version(cli_runner, version_cli_flag): - """Verify correct version output by `cookiecutter` on cli invocation.""" + """Verify Cookiecutter version output by `cookiecutter` on cli invocation.""" result = cli_runner(version_cli_flag) assert result.exit_code == 0 assert result.output.startswith('Cookiecutter') -def test_version_msg(cli_runner, version_cli_flag): - """Verify correct output for Cookiecutter Python version.""" +def test_cli_version_python(cli_runner, version_cli_flag): + """Verify correct Python version output by `cookiecutter` on cli invocation.""" result = cli_runner(version_cli_flag) python_major_number = sys.version_info.major python_minor_number = sys.version_info.minor @@ -66,12 +66,7 @@ def test_version_msg(cli_runner, version_cli_flag): version_output_string = version_output_string[-5:-1] if version_output_string[0] == ' ': version_output_string = version_output_string[1:] - if version_output_string[-1] == ')': - version_output_string = version_output_string[:-1] - assert ( - str(python_major_number) + '.' + str(python_minor_number) - == version_output_string - ) + assert f'{python_major_number}.{python_minor_number}' == version_output_string @pytest.mark.usefixtures('make_fake_project_dir', 'remove_fake_project_dir') From 8a7a6f51688940a67c397de0396d51a696dbcece Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Sat, 18 Dec 2021 22:56:09 +0530 Subject: [PATCH 034/202] Prints the whole sys.version string Co-authored-by: Sahil --- cookiecutter/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py index 385e024c5..6b3c583ad 100644 --- a/cookiecutter/cli.py +++ b/cookiecutter/cli.py @@ -25,7 +25,7 @@ def version_msg(): """Return the Cookiecutter version, location and Python powering it.""" - python_version = f'{sys.version_info.major}.{sys.version_info.minor}' + python_version = sys.version location = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) message = 'Cookiecutter %(version)s from {} (Python {})' return message.format(location, python_version) From cfef5740e47ec5be22be6edb51b4d919a5a724ad Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Sat, 18 Dec 2021 23:11:23 +0530 Subject: [PATCH 035/202] Removed python_version_test Co-authored-by: Sahil --- tests/test_cli.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 180314373..6bf093522 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -57,18 +57,6 @@ def test_cli_version(cli_runner, version_cli_flag): assert result.output.startswith('Cookiecutter') -def test_cli_version_python(cli_runner, version_cli_flag): - """Verify correct Python version output by `cookiecutter` on cli invocation.""" - result = cli_runner(version_cli_flag) - python_major_number = sys.version_info.major - python_minor_number = sys.version_info.minor - version_output_string = result.output[:-1] - version_output_string = version_output_string[-5:-1] - if version_output_string[0] == ' ': - version_output_string = version_output_string[1:] - assert f'{python_major_number}.{python_minor_number}' == version_output_string - - @pytest.mark.usefixtures('make_fake_project_dir', 'remove_fake_project_dir') def test_cli_error_on_existing_output_directory(cli_runner): """Test cli invocation without `overwrite-if-exists` fail if dir exist.""" From 11743af681e37a998748f69d646661b7a2043ff0 Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Sat, 18 Dec 2021 23:17:36 +0530 Subject: [PATCH 036/202] Fixed linting errors Co-authored-by: Sahil --- tests/test_cli.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 6bf093522..19740ef26 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,8 +4,6 @@ import os import re -import sys - import pytest from click.testing import CliRunner From db5fce2ee25b5ece417c12dddfc712beaace61fe Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Mon, 27 Dec 2021 23:16:19 +0530 Subject: [PATCH 037/202] Removed changes related to setuptools_scm --- .github/workflows/main.yml | 2 - pyproject.toml | 12 ------ setup.cfg | 77 +---------------------------------- setup.py | 83 ++++++++++++++++++++++++++++++++------ tox.ini | 25 ------------ 5 files changed, 71 insertions(+), 128 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 12dce4e24..8fb12c2b4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,8 +24,6 @@ jobs: pip install tox virtualenv - name: Lint run: "tox -e lint" - - name: Packaging - run: "tox -e packaging" - name: Safety run: "tox -e safety" build: diff --git a/pyproject.toml b/pyproject.toml index 26bea3f21..2240e4443 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,17 +1,5 @@ -[build-system] -requires = [ - "setuptools >= 42.0.0", # required by pyproject+setuptools_scm integration - "setuptools_scm[toml] >= 3.5.0", # required for "no-local-version" scheme - "setuptools_scm_git_archive >= 1.0", - "wheel", -] -build-backend = "setuptools.build_meta" - [tool.black] skip-string-normalization = true exclude = '/(tests/hooks-abort-render/hooks|docs\/HelloCookieCutter1)/' line-length = 88 target-version = ['py39'] - -[tool.setuptools_scm] -local_scheme = "no-local-version" diff --git a/setup.cfg b/setup.cfg index e1ddc4fbb..6d65158d3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,78 +1,3 @@ -[metadata] -name = cookiecutter -url = https://github.com/cookiecutter/cookiecutter -project_urls = - Bug Tracker = https://github.com/cookiecutter/cookiecutter/issues - CI: GitHub = https://github.com/cookiecutter/cookiecutter/actions - Documentation = https://cookiecutter.readthedocs.io/ - Source Code = https://github.com/cookiecutter/cookiecutter -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 = file: README.md -long_description_content_type = text/markdown -author = Audrey Roy Greenfeld -author_email = audreyr@gmail.com -maintainer = Audrey Roy Greenfeld -maintainer_email = audreyr@gmail.com -license = BSD -license_file = LICENSE -classifiers = - Development Status :: 5 - Production/Stable - Environment :: Console - Intended Audience :: Developers - Natural Language :: English - License :: OSI Approved :: BSD License - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: Implementation :: CPython - Programming Language :: Python - Topic :: Software Development -keywords = - cookiecutter - Python - projects - project templates - Jinja2 - skeleton - scaffolding - project directory - package - packaging - -[options] -use_scm_version = True -python_requires = >=3.6 -; package_dir = -; = src -packages = cookiecutter -zip_safe = False - -# These are required during `setup.py` run: -setup_requires = - setuptools_scm>=1.15.0 - setuptools_scm_git_archive>=1.0 - -install_requires = - binaryornot>=0.4.4 - Jinja2>=2.7,<4.0.0 - click>=7.0,<9.0.0 - pyyaml>=5.3.1 - jinja2-time>=0.2.0 - python-slugify>=4.0.0 - requests>=2.23.0 - -[options.entry_points] -console_scripts = - cookiecutter = cookiecutter.__main__:main - -[flake8] -ignore = BLK100,E231,W503 - # Excludes due to known issues or incompatibilities with black: # BLK100: Black would make changes. https://pypi.org/project/flake8-black/ # W503: https://github.com/psf/black/search?q=W503&unscoped_q=W503 @@ -83,7 +8,7 @@ statistics = 1 max-line-length = 88 [bdist_wheel] -universal = false +universal = 1 [tool:pytest] testpaths = tests diff --git a/setup.py b/setup.py index 5a2d616e8..c2db2f187 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,70 @@ -#! /usr/bin/env python3 -"""cookiecutter distutils configuration. - -The presence of this file ensures the support -of pip editable mode *with setuptools only*. -""" -import setuptools - -# https://github.com/jazzband/pip-tools/issues/1278 -setuptools.setup( - use_scm_version={"local_scheme": "no-local-version"}, - setup_requires=["setuptools_scm[toml]>=3.5.0"], -) +#!/usr/bin/env python +"""cookiecutter distutils configuration.""" +from setuptools import setup + +version = "2.0.0" + +with open('README.md', encoding='utf-8') as readme_file: + readme = readme_file.read() + +requirements = [ + 'binaryornot>=0.4.4', + 'Jinja2>=2.7,<4.0.0', + 'click>=7.0,<8.0.0', + 'pyyaml>=5.3.1', + 'jinja2-time>=0.2.0', + 'python-slugify>=4.0.0', + 'requests>=2.23.0', +] + +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=readme, + long_description_content_type='text/markdown', + author='Audrey Feldroy', + author_email='audreyr@gmail.com', + url='https://github.com/cookiecutter/cookiecutter', + packages=['cookiecutter'], + package_dir={'cookiecutter': 'cookiecutter'}, + entry_points={'console_scripts': ['cookiecutter = cookiecutter.__main__:main']}, + include_package_data=True, + python_requires='>=3.6', + install_requires=requirements, + 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 :: 3 :: Only", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Programming Language :: Python", + "Topic :: Software Development", + ], + keywords=[ + "cookiecutter", + "Python", + "projects", + "project templates", + "Jinja2", + "skeleton", + "scaffolding", + "project directory", + "package", + "packaging", + ], +) \ No newline at end of file diff --git a/tox.ini b/tox.ini index b8dd5c5e1..4cde29296 100644 --- a/tox.ini +++ b/tox.ini @@ -42,28 +42,3 @@ commands = safety check --full-report deps = safety - -[testenv:packaging] -description = - Build package, verify metadata, install package and assert behavior when ansible is missing. -deps = - build - twine -skip_install = true -commands = - {envpython} -c 'import os.path, shutil, sys; \ - dist_dir = os.path.join("{toxinidir}", "dist"); \ - os.path.isdir(dist_dir) or sys.exit(0); \ - print("Removing \{!s\} contents...".format(dist_dir), file=sys.stderr); \ - shutil.rmtree(dist_dir)' - # build using moder python build (PEP-517) - {envpython} -m build \ - --sdist \ - --wheel \ - --outdir {toxinidir}/dist/ \ - {toxinidir} - # Validate metadata using twine - twine check {toxinidir}/dist/* - # Install the wheel - sh -c "python3 -m pip install {toxinidir}/dist/*.whl" -whitelist_externals = sh From c578dce794956641b5f3baa86e804b0ccad1276f Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Mon, 27 Dec 2021 23:29:39 +0530 Subject: [PATCH 038/202] Fixed flake8 section in setup.cfg --- setup.cfg | 3 ++- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 6d65158d3..938b39de6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,8 +1,9 @@ +[flake8] # Excludes due to known issues or incompatibilities with black: # BLK100: Black would make changes. https://pypi.org/project/flake8-black/ # W503: https://github.com/psf/black/search?q=W503&unscoped_q=W503 # E231: https://github.com/psf/black/issues/1202 - +ignore = BLK100,E231,W503 statistics = 1 # black official is 88 max-line-length = 88 diff --git a/setup.py b/setup.py index c2db2f187..654010fa0 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ install_requires=requirements, license='BSD', zip_safe=False, - classifiers=[ + classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", @@ -67,4 +67,4 @@ "package", "packaging", ], -) \ No newline at end of file +) From 333b6ef9f9d898cb1f2d1363acad4519d8449004 Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Mon, 27 Dec 2021 10:23:48 -0800 Subject: [PATCH 039/202] Release 2.0.2 --- HISTORY.md | 6 +++++- cookiecutter/__init__.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 0760c1d2b..d9b1a5a28 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,10 @@ History is important, but our current roadmap can be found [here](https://github.com/cookiecutter/cookiecutter/projects) +## 2.0.2 (2021-12-27) + +* Fix Python version number in cookiecutter --version and test on Python 3.10 (#1621) @ozer550 + ## 2.0.1 (2021-12-11) ### Breaking Changes @@ -91,7 +95,7 @@ History is important, but our current roadmap can be found [here](https://github ### This release was made possible by our wonderful contributors: -@Cadair, @Casyfill, @Cy-dev-tex, @HosamAlmoghraby, @SharpEdgeMarshall, @agateau, @audreyfeldroy, @brettcannon, @chrisbrake, @cjolowicz, @dHannasch, @gliptak, @glumia, @graue70, @grrlic, @insspb, @javiersanp, @jonaswre, @jsoref, @Jthevos, @juhuebner, @logworthy, @lyz-code, @milonimrod, @ndclt, @noirbizarre, @noone234, @oncleben31, @ozer619, @rgreinho, @sebix, @Sahil-101, @simobasso, @smoothml, @ssbarnea, @steltenpower, @wouterdb, @xyb, Christopher Wolfe and Hosam Almoghraby ( RIAG Digital ) +@Cadair, @Casyfill, @Cy-dev-tex, @HosamAlmoghraby, @SharpEdgeMarshall, @agateau, @audreyfeldroy, @brettcannon, @chrisbrake, @cjolowicz, @dHannasch, @gliptak, @glumia, @graue70, @grrlic, @insspb, @javiersanp, @jonaswre, @jsoref, @Jthevos, @juhuebner, @logworthy, @lyz-code, @milonimrod, @ndclt, @noirbizarre, @noone234, @oncleben31, @ozer550, @rgreinho, @sebix, @Sahil-101, @simobasso, @smoothml, @ssbarnea, @steltenpower, @wouterdb, @xyb, Christopher Wolfe and Hosam Almoghraby ( RIAG Digital ) ## 1.7.2 (2020-04-21) diff --git a/cookiecutter/__init__.py b/cookiecutter/__init__.py index fd2954f2e..eab68df8e 100644 --- a/cookiecutter/__init__.py +++ b/cookiecutter/__init__.py @@ -1,2 +1,2 @@ """Main package for Cookiecutter.""" -__version__ = "2.0.1" +__version__ = "2.0.2" From 0b406256d2c0aa728f860692d57f49c0d2beb39d Mon Sep 17 00:00:00 2001 From: Audrey Roy Greenfeld Date: Mon, 27 Dec 2021 10:28:18 -0800 Subject: [PATCH 040/202] Update 2.0.2 release notes --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index d9b1a5a28..5fc9fd5a9 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,6 +5,7 @@ History is important, but our current roadmap can be found [here](https://github ## 2.0.2 (2021-12-27) * Fix Python version number in cookiecutter --version and test on Python 3.10 (#1621) @ozer550 +* Removed changes related to setuptools_scm (#1629) @audreyfeldroy @ozer550 ## 2.0.1 (2021-12-11) ### Breaking Changes From c0e7698097d905938cd9ecd1a08d9a2c99c4f887 Mon Sep 17 00:00:00 2001 From: Marco Westerhof Date: Fri, 18 Mar 2022 17:04:19 +0100 Subject: [PATCH 041/202] Feature/local extensions (#1240) Co-authored-by: Andrey Shpak --- cookiecutter/main.py | 47 ++++++++++----- cookiecutter/utils.py | 13 ++++ docs/advanced/index.rst | 1 + docs/advanced/local_extensions.rst | 60 +++++++++++++++++++ .../local_extension/cookiecutter.json | 10 ++++ .../local_extensions/__init__.py | 1 + .../local_extension/local_extensions/main.py | 21 +++++++ .../{{cookiecutter.project_slug}}/HISTORY.rst | 8 +++ tests/test_cli.py | 31 ++++++++++ 9 files changed, 178 insertions(+), 14 deletions(-) create mode 100644 docs/advanced/local_extensions.rst create mode 100644 tests/test-extensions/local_extension/cookiecutter.json create mode 100644 tests/test-extensions/local_extension/local_extensions/__init__.py create mode 100644 tests/test-extensions/local_extension/local_extensions/main.py create mode 100644 tests/test-extensions/local_extension/{{cookiecutter.project_slug}}/HISTORY.rst diff --git a/cookiecutter/main.py b/cookiecutter/main.py index 7c9eef731..bc2f262df 100644 --- a/cookiecutter/main.py +++ b/cookiecutter/main.py @@ -4,8 +4,10 @@ The code in this module is also a good example of how to use Cookiecutter as a library rather than a script. """ +from copy import copy import logging import os +import sys from cookiecutter.config import get_user_config from cookiecutter.exceptions import InvalidModeException @@ -73,15 +75,17 @@ def cookiecutter( password=password, directory=directory, ) + import_patch = _patch_import_path_for_repo(repo_dir) template_name = os.path.basename(os.path.abspath(repo_dir)) if replay: - if isinstance(replay, bool): - context = load(config_dict['replay_dir'], template_name) - else: - path, template_name = os.path.split(os.path.splitext(replay)[0]) - context = load(path, template_name) + with import_patch: + if isinstance(replay, bool): + context = load(config_dict['replay_dir'], template_name) + else: + path, template_name = os.path.split(os.path.splitext(replay)[0]) + context = load(path, template_name) else: context_file = os.path.join(repo_dir, 'cookiecutter.json') logger.debug('context_file is %s', context_file) @@ -94,7 +98,8 @@ def cookiecutter( # 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) + with import_patch: + context['cookiecutter'] = prompt_for_config(context, no_input) # include template dir or url in the context dict context['cookiecutter']['_template'] = template @@ -105,17 +110,31 @@ def cookiecutter( dump(config_dict['replay_dir'], template_name, context) # Create project from local context and project template. - result = generate_files( - repo_dir=repo_dir, - context=context, - overwrite_if_exists=overwrite_if_exists, - skip_if_file_exists=skip_if_file_exists, - output_dir=output_dir, - accept_hooks=accept_hooks, - ) + with import_patch: + result = generate_files( + repo_dir=repo_dir, + context=context, + overwrite_if_exists=overwrite_if_exists, + skip_if_file_exists=skip_if_file_exists, + output_dir=output_dir, + accept_hooks=accept_hooks, + ) # Cleanup (if required) if cleanup: rmtree(repo_dir) return result + + +class _patch_import_path_for_repo: + def __init__(self, repo_dir): + self._repo_dir = repo_dir + self._path = None + + def __enter__(self): + self._path = copy(sys.path) + sys.path.append(self._repo_dir) + + def __exit__(self, type, value, traceback): + sys.path = self._path diff --git a/cookiecutter/utils.py b/cookiecutter/utils.py index 19b727a52..ef533171a 100644 --- a/cookiecutter/utils.py +++ b/cookiecutter/utils.py @@ -8,6 +8,7 @@ import sys from cookiecutter.prompt import read_user_yes_no +from jinja2.ext import Extension logger = logging.getLogger(__name__) @@ -105,3 +106,15 @@ def prompt_and_delete(path, no_input=False): return False sys.exit() + + +def simple_filter(filter_function): + """Decorate a function to wrap it in a simplified jinja2 extension.""" + + class SimpleFilterExtension(Extension): + def __init__(self, environment): + super(SimpleFilterExtension, self).__init__(environment) + environment.filters[filter_function.__name__] = filter_function + + SimpleFilterExtension.__name__ = filter_function.__name__ + return SimpleFilterExtension diff --git a/docs/advanced/index.rst b/docs/advanced/index.rst index 90e7f2933..66d5faadd 100644 --- a/docs/advanced/index.rst +++ b/docs/advanced/index.rst @@ -23,3 +23,4 @@ Various advanced topics regarding cookiecutter usage. template_extensions directories new_line_characters + local_extensions diff --git a/docs/advanced/local_extensions.rst b/docs/advanced/local_extensions.rst new file mode 100644 index 000000000..bf87caf7b --- /dev/null +++ b/docs/advanced/local_extensions.rst @@ -0,0 +1,60 @@ +.. _`template extensions`: + +Local Extensions +---------------- + +*New in Cookiecutter X.x* + +A template may extend the Cookiecutter environment with local extensions. +These can be part of the template itself, providing it with more sophisticated custom tags and filters. + +To do so, a template author must specify the required extensions in ``cookiecutter.json`` as follows: + +.. code-block:: json + + { + "project_slug": "Foobar", + "year": "{% now 'utc', '%Y' %}", + "_extensions": ["local_extensions.FoobarExtension"] + } + +This example assumes that a ``local_extensions`` folder (python module) exists in the template root. +It will contain a ``main.py`` file, containing the following (for instance): + +.. code-block:: python + + # -*- coding: utf-8 -*- + + from jinja2.ext import Extension + + + class FoobarExtension(Extension): + def __init__(self, environment): + super(FoobarExtension, self).__init__(environment) + environment.filters['foobar'] = lambda v: v * 2 + +This will register the ``foobar`` filter for the template. + +For many cases, this will be unneccessarily complicated. It's likely that we'd only want to register a single function +as a filter. For this, we can use the ``simple_filter`` decorator: + +.. code-block:: json + + { + "project_slug": "Foobar", + "year": "{% now 'utc', '%Y' %}", + "_extensions": ["local_extensions.simplefilterextension"] + } + +.. code-block:: python + + # -*- coding: utf-8 -*- + + from cookiecutter.utils import simple_filter + + + @simple_filter + def simplefilterextension(v): + return v * 2 + +This snippet will achieve the exact same result as the previous one. diff --git a/tests/test-extensions/local_extension/cookiecutter.json b/tests/test-extensions/local_extension/cookiecutter.json new file mode 100644 index 000000000..8141fd508 --- /dev/null +++ b/tests/test-extensions/local_extension/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_slug": "Foobar", + "test_value_class_based": "{{cookiecutter.project_slug | foobar}}", + "test_value_function_based": "{{cookiecutter.project_slug | simplefilterextension}}", + "_extensions": [ + "local_extensions.simplefilterextension", + "local_extensions.FoobarExtension" + ] +} + diff --git a/tests/test-extensions/local_extension/local_extensions/__init__.py b/tests/test-extensions/local_extension/local_extensions/__init__.py new file mode 100644 index 000000000..94e854abd --- /dev/null +++ b/tests/test-extensions/local_extension/local_extensions/__init__.py @@ -0,0 +1 @@ +from .main import FoobarExtension, simplefilterextension # noqa diff --git a/tests/test-extensions/local_extension/local_extensions/main.py b/tests/test-extensions/local_extension/local_extensions/main.py new file mode 100644 index 000000000..53f6f8f95 --- /dev/null +++ b/tests/test-extensions/local_extension/local_extensions/main.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +"""Provides custom extension, exposing a ``foobar`` filter.""" + +from jinja2.ext import Extension +from cookiecutter.utils import simple_filter + + +class FoobarExtension(Extension): + """Simple jinja2 extension for cookiecutter test purposes.""" + + def __init__(self, environment): + """Foobar Extension Constructor.""" + super(FoobarExtension, self).__init__(environment) + environment.filters['foobar'] = lambda v: v * 2 + + +@simple_filter +def simplefilterextension(v): + """Provide a simple function-based filter extension.""" + return v.upper() diff --git a/tests/test-extensions/local_extension/{{cookiecutter.project_slug}}/HISTORY.rst b/tests/test-extensions/local_extension/{{cookiecutter.project_slug}}/HISTORY.rst new file mode 100644 index 000000000..8bb7c6136 --- /dev/null +++ b/tests/test-extensions/local_extension/{{cookiecutter.project_slug}}/HISTORY.rst @@ -0,0 +1,8 @@ +History +------- + +0.1.0 +----- + +First release of {{cookiecutter.test_value_class_based}} on PyPI. +{{cookiecutter.test_value_function_based}} diff --git a/tests/test_cli.py b/tests/test_cli.py index 19740ef26..ad6abd1e0 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -10,6 +10,8 @@ from cookiecutter import utils from cookiecutter.__main__ import main +from cookiecutter.environment import StrictEnvironment +from cookiecutter.exceptions import UnknownExtension from cookiecutter.main import cookiecutter @@ -412,6 +414,35 @@ def test_echo_unknown_extension_error(output_dir, cli_runner): assert 'Unable to load extension: ' in result.output +def test_local_extension(tmpdir, cli_runner): + """Test to verify correct work of extension, included in template.""" + output_dir = str(tmpdir.mkdir('output')) + template_path = 'tests/test-extensions/local_extension/' + + result = cli_runner( + '--no-input', + '--default-config', + '--output-dir', + output_dir, + template_path, + ) + assert result.exit_code == 0 + with open(os.path.join(output_dir, 'Foobar', 'HISTORY.rst')) as f: + data = f.read() + assert 'FoobarFoobar' in data + assert 'FOOBAR' in data + + +def test_local_extension_not_available(tmpdir, cli_runner): + """Test handling of included but unavailable local extension.""" + context = {'cookiecutter': {'_extensions': ['foobar']}} + + with pytest.raises(UnknownExtension) as err: + StrictEnvironment(context=context, keep_trailing_newline=True) + + assert 'Unable to load extension: ' in str(err.value) + + @pytest.mark.usefixtures('remove_fake_project_dir') def test_cli_extra_context(cli_runner): """Cli invocation replace content if called with replacement pairs.""" From 0711b74d03fce772c32090fcfcce74f4efd08f10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20La=C5=84ski?= Date: Fri, 18 Mar 2022 17:05:16 +0100 Subject: [PATCH 042/202] Restore accidentally deleted support for click 8.x (#1643) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 654010fa0..d703c3c29 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ requirements = [ 'binaryornot>=0.4.4', 'Jinja2>=2.7,<4.0.0', - 'click>=7.0,<8.0.0', + 'click>=7.0,<9.0.0', 'pyyaml>=5.3.1', 'jinja2-time>=0.2.0', 'python-slugify>=4.0.0', From 603b0365689d8b5407fed4d6475e1eb8256fdf33 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Fri, 18 Mar 2022 16:08:39 +0000 Subject: [PATCH 043/202] Update badge & links from Slack to Discord in README (#1612) Co-authored-by: Michael Joseph Co-authored-by: Sorin Sbarnea --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9b1dfe141..8e7e2b240 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![python](https://img.shields.io/pypi/pyversions/cookiecutter.svg)](https://pypi.org/project/cookiecutter/) [![Build Status](https://github.com/cookiecutter/cookiecutter/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/cookiecutter/cookiecutter/actions) [![codecov](https://codecov.io/gh/cookiecutter/cookiecutter/branch/master/graphs/badge.svg?branch=master)](https://codecov.io/github/cookiecutter/cookiecutter?branch=master) -[![slack](https://img.shields.io/badge/cookiecutter-Join%20on%20Slack-green?style=flat&logo=slack)](https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U) +[![discord](https://img.shields.io/badge/Discord-cookiecutter-5865F2?style=flat&logo=discord&logoColor=white)](https://discord.gg/9BrxzPKuEW) [![docs](https://readthedocs.org/projects/cookiecutter/badge/?version=latest)](https://readthedocs.org/projects/cookiecutter/?badge=latest) [![Code Quality](https://img.shields.io/scrutinizer/g/cookiecutter/cookiecutter.svg)](https://scrutinizer-ci.com/g/cookiecutter/cookiecutter/?branch=master) @@ -178,7 +178,7 @@ Stuck? Try one of the following: 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 [Slack](https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U) +- Ask for help on [Discord](https://discord.gg/9BrxzPKuEW) if you must (but please try one of the other options first, so that others can benefit from the discussion). @@ -189,8 +189,8 @@ Development on Cookiecutter is community-driven: - Everyone is invited to contribute. Read the [contributing instructions](CONTRIBUTING.md), then get started. - Connect with other Cookiecutter contributors and users on - [Slack](https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U) - (note: due to work and commitments, a core committer might not always be available) + [Discord](https://discord.gg/9BrxzPKuEW) + (note: due to work and other commitments, a core committer might not always be available) Encouragement is unbelievably motivating. If you want more work done on Cookiecutter, show support: From 682fe854dca3ce735d656a6588e9ea9369c4ea47 Mon Sep 17 00:00:00 2001 From: Prathamesh Desai Date: Fri, 18 Mar 2022 21:39:24 +0530 Subject: [PATCH 044/202] Improve local development step 3 (#1610) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c17a916c4..f54ae0e06 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,7 +74,7 @@ git clone git@github.com:your_name_here/cookiecutter.git ```bash cd cookiecutter/ -python setup.py develop +pip install -e . ``` 4. Create a branch for local development: From 980ff089efaef27b99b97525f6a206bd7c3f2282 Mon Sep 17 00:00:00 2001 From: Maciej Patro Date: Sat, 7 May 2022 09:55:59 +0200 Subject: [PATCH 045/202] test_generate_file_verbose_template_syntax_error fixed Fixes #1655 - relative path that comes directly from Jinja2 TemplateSyntaxError in some cases is in format "./tests/..." in other "tests/... now both cases are accepted as a valid outcome. --- tests/test_generate_file.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_generate_file.py b/tests/test_generate_file.py index 2ca30dff2..943399939 100644 --- a/tests/test_generate_file.py +++ b/tests/test_generate_file.py @@ -1,6 +1,7 @@ """Tests for `generate_file` function, part of `generate_files` function workflow.""" import json import os +import re import pytest from jinja2 import FileSystemLoader @@ -114,17 +115,16 @@ def test_generate_file_with_false_condition(env): @pytest.fixture -def expected_msg(): +def expected_msg_regex(): """Fixture. Used to ensure that exception generated text contain full data.""" - msg = ( + return re.compile( 'Missing end of comment tag\n' - ' File "./tests/files/syntax_error.txt", line 1\n' - ' I eat {{ syntax_error }} {# this comment is not closed}' + ' {2}File "(.[/\\\\])*tests[/\\\\]files[/\\\\]syntax_error.txt", line 1\n' + ' {4}I eat {{ syntax_error }} {# this comment is not closed}' ) - return msg.replace("/", os.sep) -def test_generate_file_verbose_template_syntax_error(env, expected_msg): +def test_generate_file_verbose_template_syntax_error(env, expected_msg_regex): """Verify correct exception raised on syntax error in file before generation.""" with pytest.raises(TemplateSyntaxError) as exception: generate.generate_file( @@ -133,7 +133,7 @@ def test_generate_file_verbose_template_syntax_error(env, expected_msg): context={'syntax_error': 'syntax_error'}, env=env, ) - assert str(exception.value) == expected_msg + assert expected_msg_regex.match(str(exception.value)) is not None def test_generate_file_does_not_translate_lf_newlines_to_crlf(env, tmp_path): From 6eb9ea81be1fed3d58197800630e07e14380f7a4 Mon Sep 17 00:00:00 2001 From: Maciej Patro Date: Mon, 16 May 2022 21:31:44 +0200 Subject: [PATCH 046/202] Remove redundant comparison Co-authored-by: jurgenwigg <53076001+jurgenwigg@users.noreply.github.com> --- tests/test_generate_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_generate_file.py b/tests/test_generate_file.py index 943399939..a393a2ef8 100644 --- a/tests/test_generate_file.py +++ b/tests/test_generate_file.py @@ -133,7 +133,7 @@ def test_generate_file_verbose_template_syntax_error(env, expected_msg_regex): context={'syntax_error': 'syntax_error'}, env=env, ) - assert expected_msg_regex.match(str(exception.value)) is not None + assert expected_msg_regex.match(str(exception.value)) def test_generate_file_does_not_translate_lf_newlines_to_crlf(env, tmp_path): From 05267c6aac1ee5027c422c086dfbd9fbf867b8bf Mon Sep 17 00:00:00 2001 From: Maciej Patro Date: Mon, 16 May 2022 21:42:20 +0200 Subject: [PATCH 047/202] Fix black incompatibility with click 8.1.0 version --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ce7d43cfc..1ba7fa3e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: files: \.rst$ require_serial: true - repo: https://github.com/psf/black.git - rev: 21.10b0 + rev: 22.3.0 hooks: - id: black language_version: python3 From 2b6de977046b8d3aeed7f2fd4d1841b59e6396f9 Mon Sep 17 00:00:00 2001 From: Daniel Roy Greenfeld Date: Tue, 24 May 2022 10:37:05 -0700 Subject: [PATCH 048/202] Add Audrey Roy Greenfeld as creator and leader --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8e7e2b240..6a153c097 100644 --- a/README.md +++ b/README.md @@ -233,3 +233,7 @@ organizations and individuals to support the project. Everyone interacting in the Cookiecutter project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [PyPA Code of Conduct](https://www.pypa.io/en/latest/code-of-conduct/). + +## Creator / Leader + +This project was created and is led by [Audrey Roy Greenfeld](https://github.com/audreyfeldroy). From 43ccfcc8e6cfdc41644119678a827b9c1cf524e6 Mon Sep 17 00:00:00 2001 From: Daniel Roy Greenfeld Date: Tue, 24 May 2022 10:37:19 -0700 Subject: [PATCH 049/202] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 6a153c097..a954feca2 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,6 @@ template. ![Cookiecutter](https://raw.githubusercontent.com/cookiecutter/cookiecutter/3ac078356adf5a1a72042dfe72ebfa4a9cd5ef38/logo/cookiecutter_medium.png) -We are proud to be an open source sponsor of -[PyCon 2016](https://us.pycon.org/2016/sponsors/). - ## Features - Cross-platform: Windows, Mac, and Linux are officially supported. From 050246683e2b95f35898c013fff6d14a51c2b5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20C=2E=20Barrionuevo=20da=20Luz?= Date: Sun, 29 May 2022 16:08:17 -0300 Subject: [PATCH 050/202] Fixed incorrect link on docs. (#1649) Close #1648 --- docs/advanced/choice_variables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/choice_variables.rst b/docs/advanced/choice_variables.rst index c86eb11c1..ccb35d4bf 100644 --- a/docs/advanced/choice_variables.rst +++ b/docs/advanced/choice_variables.rst @@ -39,7 +39,7 @@ can be used like this:: {% endif %} -Cookiecutter is using `Jinja2's if conditional expression `_ to determine the correct license. +Cookiecutter is using `Jinja2's if conditional expression `_ to determine the correct license. The created choice variable is still a regular Cookiecutter variable and can be used like this:: From 08fbbee72d3a21431f93bd534432939c2d2999b0 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 01:59:29 +0200 Subject: [PATCH 051/202] Documentation overhaul. --- .gitmodules | 3 - HISTORY.md | 9 +- README.md | 212 ++++++++++++-------------- docs/HelloCookieCutter1 | 1 - docs/advanced/index.rst | 1 - docs/advanced/local_extensions.rst | 2 +- docs/advanced/template_extensions.rst | 2 +- docs/advanced/user_config.rst | 39 +++-- docs/{advanced => }/cli_options.rst | 0 docs/conf.py | 6 +- docs/index.rst | 5 +- docs/installation.rst | 34 +++-- docs/overview.rst | 16 +- docs/tutorials.rst | 33 ---- docs/tutorials/index.rst | 36 +++++ docs/{ => tutorials}/tutorial1.rst | 2 +- docs/{ => tutorials}/tutorial2.rst | 0 17 files changed, 201 insertions(+), 200 deletions(-) delete mode 100644 .gitmodules delete mode 160000 docs/HelloCookieCutter1 rename docs/{advanced => }/cli_options.rst (100%) delete mode 100644 docs/tutorials.rst create mode 100644 docs/tutorials/index.rst rename docs/{ => tutorials}/tutorial1.rst (99%) rename docs/{ => tutorials}/tutorial2.rst (100%) diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e0372d2b5..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "docs/HelloCookieCutter1"] - path = docs/HelloCookieCutter1 - url = https://github.com/BruceEckel/HelloCookieCutter1 diff --git a/HISTORY.md b/HISTORY.md index 5fc9fd5a9..b39699c1b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,10 +4,15 @@ History is important, but our current roadmap can be found [here](https://github ## 2.0.2 (2021-12-27) +*Remark: This release never made it to official PyPI* + * Fix Python version number in cookiecutter --version and test on Python 3.10 (#1621) @ozer550 * Removed changes related to setuptools_scm (#1629) @audreyfeldroy @ozer550 ## 2.0.1 (2021-12-11) + +*Remark: This release never made it to official PyPI* + ### Breaking Changes * Release preparation for 2.0.1rc1 (#1608) @audreyfeldroy @@ -105,7 +110,7 @@ History is important, but our current roadmap can be found [here](https://github ## 1.7.1 (2020-04-21) This release was focused on internal code and CI/CD changes. During this release -all code was verified to match pep8, pep257 and other code-styling guides. +all code was verified to match pep8, pep257 and other code-styling guides. Project CI/CD was significantly changed, Windows platform checks based on Appveyor engine was replaced by GitHub actions tests. Appveyor was removed. Also our CI/CD was extended with Mac builds, to verify project builds on Apple devices. @@ -773,7 +778,7 @@ Other changes: $ 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: diff --git a/README.md b/README.md index a954feca2..493f63cc4 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,8 @@ [![docs](https://readthedocs.org/projects/cookiecutter/badge/?version=latest)](https://readthedocs.org/projects/cookiecutter/?badge=latest) [![Code Quality](https://img.shields.io/scrutinizer/g/cookiecutter/cookiecutter.svg)](https://scrutinizer-ci.com/g/cookiecutter/cookiecutter/?branch=master) -A command-line utility that creates projects from **cookiecutters** (project -templates), e.g. creating a Python package project from a Python package project -template. +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: [https://cookiecutter.readthedocs.io](https://cookiecutter.readthedocs.io) - GitHub: [https://github.com/cookiecutter/cookiecutter](https://github.com/cookiecutter/cookiecutter) @@ -23,64 +22,44 @@ template. - Cross-platform: Windows, Mac, and Linux are officially supported. - You don't have to know/write Python code to use Cookiecutter. -- Works with Python 3.7, 3.8, 3.9. +- Works with Python 3.7, 3.8, 3.9., 3.10 - 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. + +### For users of existing templates + - Simple command line usage: -```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/audreyfeldroy/cookiecutter-pypackage -# For the sake of brevity, repos on GitHub can just use the 'gh' prefix -$ cookiecutter gh:audreyfeldroy/cookiecutter-pypackage -``` + ```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/audreyfeldroy/cookiecutter-pypackage + # For the sake of brevity, repos on GitHub can just use the 'gh' prefix + $ cookiecutter gh:audreyfeldroy/cookiecutter-pypackage + ``` - Use it at the command line with a local template: -```bash -# Create project in the current working directory, from the local -# cookiecutter-pypackage/ template -$ cookiecutter cookiecutter-pypackage/ -``` + ```bash + # Create project in the current working directory, from the local + # cookiecutter-pypackage/ template + $ cookiecutter cookiecutter-pypackage/ + ``` - Or use it from Python: -```py -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/audreyfeldroy/cookiecutter-pypackage.git') -``` - -- Directory names and filenames can be templated. For example: + ```py + from cookiecutter.main import cookiecutter -```py -{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}.py -``` + # Create project from the cookiecutter-pypackage/ template + cookiecutter('cookiecutter-pypackage/') -- 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: - -```json -{ - "full_name": "Audrey Roy Greenfeld", - "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" -} -``` + # Create project from the cookiecutter-pypackage.git repo template + cookiecutter('https://github.com/audreyfeldroy/cookiecutter-pypackage.git') + ``` - Unless you suppress it with `--no-input`, you are prompted for input: - Prompts are the keys in `cookiecutter.json`. @@ -88,78 +67,93 @@ cookiecutter('https://github.com/audreyfeldroy/cookiecutter-pypackage.git') - Prompts are shown in order. - Cross-platform support for `~/.cookiecutterrc` files: -```yaml -default_context: - full_name: "Audrey Roy Greenfeld" - email: "audreyr@gmail.com" - github_username: "audreyfeldroy" -cookiecutters_dir: "~/.cookiecutters/" -``` - -- Cookiecutters (cloned Cookiecutter project templates) are put into - `~/.cookiecutters/` by default, or cookiecutters_dir if specified. -- If you have already cloned a cookiecutter into `~/.cookiecutters/`, - you can reference it by directory name: - -```bash -# Clone cookiecutter-pypackage -$ cookiecutter gh:audreyfeldroy/cookiecutter-pypackage -# Now you can use the already cloned cookiecutter by name -$ cookiecutter cookiecutter-pypackage -``` - -- 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. + ```yaml + default_context: + full_name: "Audrey Roy Greenfeld" + email: "audreyr@gmail.com" + github_username: "audreyfeldroy" + cookiecutters_dir: "~/.cookiecutters/" + ``` + +- Cookiecutters (cloned Cookiecutter project templates) are put into `~/.cookiecutters/` by default, or cookiecutters_dir if specified. +- If you have already cloned a cookiecutter into `~/.cookiecutters/`, you can reference it by directory name: + + ```bash + # Clone cookiecutter-pypackage + $ cookiecutter gh:audreyfeldroy/cookiecutter-pypackage + # Now you can use the already cloned cookiecutter by name + $ cookiecutter cookiecutter-pypackage + ``` + +- 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. - Inject extra context with command-line arguments: -```bash -cookiecutter --no-input gh:msabramo/cookiecutter-supervisor program_name=foobar startsecs=10 -``` + ```bash + cookiecutter --no-input gh:msabramo/cookiecutter-supervisor program_name=foobar startsecs=10 + ``` - 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 generated to your current directory or to target directory if - specified with `-o` option. +- Projects generated to your current directory or to target directory if specified with `-o` option. + +### For template creators + +- Supports unlimited levels of directory nesting. +- 100% of templating is done with Jinja2. +- Both, directory names and filenames can be templated. + For example: + + ```py + {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}.py + ``` +- Simply define your template variables in a `cookiecutter.json` file. + For example: + + ```json + { + "full_name": "Audrey Roy Greenfeld", + "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" + } + ``` +- Pre- and post-generate hooks: Python or shell scripts to run before or after generating a project. ## Available Cookiecutters -Making great cookies takes a lot of cookiecutters and contributors. We're so -pleased that there are many Cookiecutter project templates to choose from. We -hope you find a cookiecutter that is just right for your needs. +Making great cookies takes a lot of cookiecutters and contributors. +We're so pleased that there are many Cookiecutter project templates to choose from. +We hope you find a cookiecutter that is just right for your needs. -## A Pantry Full of Cookiecutters +### A Pantry Full of Cookiecutters -The best place to start searching for specific and ready to use cookiecutter -template is [Github search](https://github.com/search?q=cookiecutter&type=Repositories). +The best place to start searching for specific and ready to use cookiecutter template is [Github search](https://github.com/search?q=cookiecutter&type=Repositories). Just type `cookiecutter` and you will discover over 4000 related repositories. -We also recommend you to check related GitHub topics. For general search use -[cookiecutter-template](https://github.com/topics/cookiecutter-template). -For specific topics try to use `cookiecutter-yourtopic`, like -`cookiecutter-python` or `cookiecutter-datascience`. This is a new GitHub feature, -so not all active repositories use it at the moment. +We also recommend you to check related GitHub topics. +For general search use [cookiecutter-template](https://github.com/topics/cookiecutter-template). +For specific topics try to use `cookiecutter-yourtopic`, like `cookiecutter-python` or `cookiecutter-datascience`. +This is a new GitHub feature, so not all active repositories use it at the moment. -If you are template developer please add related -[topics](https://help.github.com/en/github/administering-a-repository/classifying-your-repository-with-topics) -with `cookiecutter` prefix to you repository. We believe it will make it more -discoverable. You are almost not limited in topics amount, use it! +If you are template developer please add related [topics](https://help.github.com/en/github/administering-a-repository/classifying-your-repository-with-topics) with `cookiecutter` prefix to you repository. +We believe it will make it more discoverable. +You are almost not limited in topics amount, use it! -## Cookiecutter Specials +### Cookiecutter Specials These Cookiecutters are maintained by the cookiecutter team: - [cookiecutter-pypackage](https://github.com/audreyfeldroy/cookiecutter-pypackage): [@audreyfeldroy's](https://github.com/audreyfeldroy) ultimate Python package project template. - [cookiecutter-django](https://github.com/pydanny/cookiecutter-django): - A bleeding edge Django project template with Bootstrap 4, customizable users app, - starter templates, working user registration, celery setup, and much more. + A bleeding edge Django project template with Bootstrap 4, customizable users app, starter templates, working user registration, celery setup, and much more. - [cookiecutter-pytest-plugin](https://github.com/pytest-dev/cookiecutter-pytest-plugin): - Minimal Cookiecutter template for authoring [pytest](https://docs.pytest.org/) - plugins that help you to write better programs. + Minimal Cookiecutter template for authoring [pytest](https://docs.pytest.org/) plugins that help you to write better programs. ## Community @@ -171,22 +165,16 @@ Stuck? Try one of the following: - See the [Troubleshooting](https://cookiecutter.readthedocs.io/en/latest/troubleshooting.html) page. - Ask for help on [Stack Overflow](https://stackoverflow.com/questions/tagged/cookiecutter). - You are strongly encouraged to - [file an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) - 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 [Discord](https://discord.gg/9BrxzPKuEW) - if you must (but please try one of the other options first, so that others - can benefit from the discussion). + [file an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) 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 [Discord](https://discord.gg/9BrxzPKuEW) 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](AUTHORS.md) who have pitched in to help - make Cookiecutter an even better tool. -- Everyone is invited to contribute. Read the - [contributing instructions](CONTRIBUTING.md), then get started. -- Connect with other Cookiecutter contributors and users on - [Discord](https://discord.gg/9BrxzPKuEW) +- Huge thanks to all the [contributors](AUTHORS.md) who have pitched in to help make Cookiecutter an even better tool. +- Everyone is invited to contribute. + Read the [contributing instructions](CONTRIBUTING.md), then get started. +- Connect with other Cookiecutter contributors and users on [Discord](https://discord.gg/9BrxzPKuEW) (note: due to work and other commitments, a core committer might not always be available) Encouragement is unbelievably motivating. If you want more work done on diff --git a/docs/HelloCookieCutter1 b/docs/HelloCookieCutter1 deleted file mode 160000 index 239ea6928..000000000 --- a/docs/HelloCookieCutter1 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 239ea692896301eaa280dd407fdd4d5c55cf6998 diff --git a/docs/advanced/index.rst b/docs/advanced/index.rst index 66d5faadd..d2111ad4b 100644 --- a/docs/advanced/index.rst +++ b/docs/advanced/index.rst @@ -17,7 +17,6 @@ Various advanced topics regarding cookiecutter usage. private_variables copy_without_render replay - cli_options choice_variables dict_variables template_extensions diff --git a/docs/advanced/local_extensions.rst b/docs/advanced/local_extensions.rst index bf87caf7b..a90b04d7d 100644 --- a/docs/advanced/local_extensions.rst +++ b/docs/advanced/local_extensions.rst @@ -1,4 +1,4 @@ -.. _`template extensions`: +.. _`local extensions`: Local Extensions ---------------- diff --git a/docs/advanced/template_extensions.rst b/docs/advanced/template_extensions.rst index 940b87b2f..2e2273e8b 100644 --- a/docs/advanced/template_extensions.rst +++ b/docs/advanced/template_extensions.rst @@ -96,7 +96,7 @@ that converts string into its underscored ("slugified") version: Would output: -.. code-block:: json +:: it-s-a-random-version diff --git a/docs/advanced/user_config.rst b/docs/advanced/user_config.rst index 96f500898..a46dc95b6 100644 --- a/docs/advanced/user_config.rst +++ b/docs/advanced/user_config.rst @@ -3,9 +3,8 @@ 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. +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``:: @@ -15,9 +14,8 @@ 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. +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: @@ -36,18 +34,17 @@ Example user config: 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 :ref:`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), `bb` (bitbucket), and `gl` (gitlab) abbreviations shown - above are actually built in, and can be used without defining them yourself. +``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 + :ref:`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), ``bb`` (Bitbucket), and ``gl`` (Gitlab) abbreviations shown above are actually **built in**, and can be used without defining them yourself. diff --git a/docs/advanced/cli_options.rst b/docs/cli_options.rst similarity index 100% rename from docs/advanced/cli_options.rst rename to docs/cli_options.rst diff --git a/docs/conf.py b/docs/conf.py index 082a25d03..51648bd10 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -95,7 +95,7 @@ def __getattr__(cls, name): # General information about the project. project = 'cookiecutter' -copyright = '2013-2019, Audrey Roy and Cookiecutter community' +copyright = '2013-2022, Audrey Roy and Cookiecutter community' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -324,7 +324,7 @@ def __getattr__(cls, name): epub_title = 'cookiecutter' epub_author = 'Audrey Roy' epub_publisher = 'Audrey Roy and Cookiecutter community' -epub_copyright = '2013-2019, Audrey Roy and Cookiecutter community' +epub_copyright = '2013-2022, Audrey Roy and Cookiecutter community' # The language of the text. It defaults to the language option # or en if the language is not set. @@ -377,4 +377,4 @@ def __getattr__(cls, name): # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} +intersphinx_mapping = {'https://docs.python.org/3': None} diff --git a/docs/index.rst b/docs/index.rst index 15a953e63..1a391b521 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,9 +18,8 @@ Basics overview installation usage - tutorials - tutorial1 - tutorial2 + cli_options + tutorials/index advanced/index troubleshooting diff --git a/docs/installation.rst b/docs/installation.rst index d12709c37..e4084ab81 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -12,9 +12,13 @@ Prerequisites Python interpreter ^^^^^^^^^^^^^^^^^^ -Install Python for your operating system. Consult the official `Python documentation `_ for details. +Install Python for your operating system. +On Windows and macOS this is usually necessary. +Most Linux distributions are coming with Python pre-installed. +Consult the official `Python documentation `_ for details. -You can install the Python binaries from `python.org `_. Alternatively on macOS, you can use the `homebrew `_ package manager. +You can install the Python binaries from `python.org `_. +Alternatively on macOS, you can use the `homebrew `_ package manager. .. code-block:: bash @@ -77,7 +81,7 @@ Or, if you do not have pip: $ easy_install --user cookiecutter -Though, pip is recommended. +Though, pip is recommended, easy_install is deprecated. Or, if you are using conda, first add conda-forge to your channels: @@ -106,32 +110,32 @@ Alternate installations $ pipx install cookiecutter -**Debian/Ubuntu:** -.. code-block:: bash - - $ sudo apt-get install cookiecutter +Upgrading +--------- -Upgrading from 0.6.4 to 0.7.0 or greater ----------------------------------------- +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: +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: + +Or with pip: .. code-block:: bash - $ easy_install --upgrade cookiecutter + $ python3 -m pip install --upgrade cookiecutter -Or with pip: +Upgrade Cookiecutter either with easy_install (deprecated): .. code-block:: bash - $ python3 -m pip install --upgrade cookiecutter + $ easy_install --upgrade cookiecutter Then you should be good to go. diff --git a/docs/overview.rst b/docs/overview.rst index 0a823305b..6b8e029ee 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -2,10 +2,20 @@ Overview ======== +Cookiecutter takes a template provided as a directory structure with template-files. +Templates can be in located in the filesystem, as a ZIP-file or on a VCS-Server (Git/Hg) like GitHub. + +It reads a settings file and prompts the user interactivly wether to change the settings. + +Then it takes both and generates an output directory structure from it. + +Additional the template can provide code (Python or shell-script) to be executed before and after generation (pre-gen- and post-gen-hooks). + + Input ----- -This is the directory structure for a simple cookiecutter:: +This is a directory structure for a simple cookiecutter:: cookiecutter-something/ ├── {{ cookiecutter.project_name }}/ <--------- Project template @@ -17,8 +27,8 @@ This is the directory structure for a simple cookiecutter:: You must have: -* A `cookiecutter.json` file. -* A `{{ cookiecutter.project_name }}/` directory, where +- A `cookiecutter.json` file. +- A `{{ cookiecutter.project_name }}/` directory, where `project_name` is defined in your `cookiecutter.json`. Beyond that, you can have whatever files/directories you want. diff --git a/docs/tutorials.rst b/docs/tutorials.rst deleted file mode 100644 index 16903235e..000000000 --- a/docs/tutorials.rst +++ /dev/null @@ -1,33 +0,0 @@ -==================== -Additional Tutorials -==================== - -Learn How to Use Cookiecutter ------------------------------ - -* :doc:`tutorial1` by `@audreyr`_ - - -Create Your Very Own Cookiecutter Project Template --------------------------------------------------- - -* :doc:`tutorial2` by `@audreyr`_ - -* `Project Templates Made Easy`_ by `@pydanny`_ - -* Cookiedozer Tutorials by `@hackebrot`_ - - * Part 1: `Create your own Cookiecutter template`_ - * Part 2: `Extending our Cookiecutter template`_ - * Part 3: `Wrapping up our Cookiecutter template`_ - - -.. _`Project Templates Made Easy`: http://www.pydanny.com/cookie-project-templates-made-easy.html - -.. _`Create your own Cookiecutter template`: https://raphael.codes/blog/create-your-own-cookiecutter-template/ -.. _`Extending our Cookiecutter template`: https://raphael.codes/blog/extending-our-cookiecutter-template/ -.. _`Wrapping up our Cookiecutter template`: https://raphael.codes/blog/wrapping-up-our-cookiecutter-template/ - -.. _`@audreyr`: https://github.com/audreyr -.. _`@pydanny`: https://github.com/pydanny -.. _`@hackebrot`: https://github.com/hackebrot diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst new file mode 100644 index 000000000..81e1b0dff --- /dev/null +++ b/docs/tutorials/index.rst @@ -0,0 +1,36 @@ +==================== +Tutorials +==================== + +Tutorials by `@audreyr`_ + +.. toctree:: + :maxdepth: 2 + + tutorial1 + tutorial2 + + +External Links +-------------- + +- `Learn the Basics of Cookiecutter by Creating a Cookiecutter`_ - first steps tutorial with example template by `@BruceEckel`_ +- `Project Templates Made Easy`_ by `@pydanny`_ +- Cookiedozer Tutorials by `@hackebrot`_ + + - Part 1: `Create your own Cookiecutter template`_ + - Part 2: `Extending our Cookiecutter template`_ + - Part 3: `Wrapping up our Cookiecutter template`_ + + +.. _`Learn the Basics of Cookiecutter by Creating a Cookiecutter`: https://github.com/BruceEckel/HelloCookieCutter1/blob/master/Readme.rst +.. _`Project Templates Made Easy`: http://www.pydanny.com/cookie-project-templates-made-easy.html + +.. _`Create your own Cookiecutter template`: https://raphael.codes/blog/create-your-own-cookiecutter-template/ +.. _`Extending our Cookiecutter template`: https://raphael.codes/blog/extending-our-cookiecutter-template/ +.. _`Wrapping up our Cookiecutter template`: https://raphael.codes/blog/wrapping-up-our-cookiecutter-template/ + +.. _`@audreyr`: https://github.com/audreyr +.. _`@pydanny`: https://github.com/pydanny +.. _`@hackebrot`: https://github.com/hackebrot +.. _`@BruceEckel`: https://github.com/BruceEckel diff --git a/docs/tutorial1.rst b/docs/tutorials/tutorial1.rst similarity index 99% rename from docs/tutorial1.rst rename to docs/tutorials/tutorial1.rst index 2659db8b2..555753609 100644 --- a/docs/tutorial1.rst +++ b/docs/tutorials/tutorial1.rst @@ -3,7 +3,7 @@ Getting to Know Cookiecutter ============================= .. note:: Before you begin, please install Cookiecutter 0.7.0 or higher. - Instructions are in :doc:`installation`. + Instructions are in :doc:`../installation`. Cookiecutter is a tool for creating projects from *cookiecutters* (project templates). diff --git a/docs/tutorial2.rst b/docs/tutorials/tutorial2.rst similarity index 100% rename from docs/tutorial2.rst rename to docs/tutorials/tutorial2.rst From e9dc60e7c11ee9f9eca0db1f01d126ecf4803e76 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 12:26:39 +0200 Subject: [PATCH 052/202] better naming in generated API docs --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 71ea835bd..2dc6aa3aa 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,10 @@ docs: ## Generate Sphinx HTML documentation, including API docs @rm -f docs/cookiecutter.rst @sphinx-apidoc -o docs/ cookiecutter @rm -f docs/modules.rst + @sed -i 's/cookiecutter package/===\nAPI\n===/' docs/cookiecutter.rst + @sed -i 's/====================//' docs/cookiecutter.rst + @sed -i 's/Submodules/This is the Cookiecutter modules API documentation./' docs/cookiecutter.rst + @sed -i 's/^----------$$//' docs/cookiecutter.rst @$(MAKE) -C docs clean @$(MAKE) -C docs html @$(BROWSER) docs/_build/html/index.html From 1f04a3e7bcd17e720aea215abae97a25fbea2c4d Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 12:27:24 +0200 Subject: [PATCH 053/202] update troove classifiers, version and required python version --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index d703c3c29..9cb220978 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ """cookiecutter distutils configuration.""" from setuptools import setup -version = "2.0.0" +version = "2.0.3.dev0" with open('README.md', encoding='utf-8') as readme_file: readme = readme_file.read() @@ -34,7 +34,7 @@ package_dir={'cookiecutter': 'cookiecutter'}, entry_points={'console_scripts': ['cookiecutter = cookiecutter.__main__:main']}, include_package_data=True, - python_requires='>=3.6', + python_requires='>=3.7', install_requires=requirements, license='BSD', zip_safe=False, @@ -46,10 +46,10 @@ "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python", From ed80c90798c575d7956fd6c7ab3e757a606cf606 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 12:28:09 +0200 Subject: [PATCH 054/202] Documentation overhaul (2) --- AUTHORS.md | 1 + CODE_OF_CONDUCT.md | 5 +- CONTRIBUTING.md | 275 +++++++++++++++++++++++------------------- README.md | 50 ++++---- docs/cookiecutter.rst | 7 +- docs/index.rst | 2 +- docs/installation.rst | 18 +-- docs/overview.rst | 5 +- 8 files changed, 190 insertions(+), 173 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index fd75a6943..d63785240 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -169,6 +169,7 @@ - Tom Forbes ([@orf](https://github.com/orf)) - Xie Yanbo ([@xyb](https://github.com/xyb)) - Maxim Ivanov ([@ivanovmg](https://github.com/ivanovmg)) +- Jens Klein ([@jensens](https://github.com/jensens)) ## Backers diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5b484056f..3043a6bab 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,5 +1,4 @@ # 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](https://www.pypa.io/en/latest/code-of-conduct/). +Everyone interacting in the Cookiecutter project's codebases and documentation is expected to follow the [PyPA Code of Conduct](https://www.pypa.io/en/latest/code-of-conduct/). +This includes, but is not limited to, issue trackers, chat rooms, mailing lists, and other virtual or in real life communication. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f54ae0e06..f0ae0bb42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,12 +1,13 @@ # Contributing -Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. +Contributions are welcome, and they are greatly appreciated! +Every little bit helps, and credit will always be given. -* [Types of Contributions](#Types-of-Contributions) -* [Contributor Setup](#Setting-Up-the-Code-for-Local-Development) -* [Contributor Guidelines](#Contributor-Guidelines) -* [Contributor Testing](#Testing-with-tox) -* [Core Committer Guide](#Core-Committer-Guide) +- [Types of Contributions](#Types-of-Contributions) +- [Contributor Setup](#Setting-Up-the-Code-for-Local-Development) +- [Contributor Guidelines](#Contributor-Guidelines) +- [Contributor Testing](#Testing-with-tox) +- [Core Committer Guide](#Core-Committer-Guide) ## Types of Contributions @@ -18,22 +19,27 @@ Report bugs at [https://github.com/cookiecutter/cookiecutter/issues](https://git 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. +- 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. +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" and "please-help" is open to whoever wants to implement it. +Look through the GitHub issues for features. +Anything tagged with "enhancement" and "please-help" is open to whoever wants to implement it. Please do not combine multiple feature enhancements into a single pull request. -Note: this project is very conservative, so new features that aren't tagged with "please-help" might not get into core. We're trying to keep the code base small, extensible, and streamlined. Whenever possible, it's best to try and implement feature ideas as separate projects outside of the core codebase. +Note: this project is very conservative, so new features that aren't tagged with "please-help" might not get into core. +We're trying to keep the code base small, extensible, and streamlined. +Whenever possible, it's best to try and implement feature ideas as separate projects outside of the core codebase. ### Write Documentation @@ -54,9 +60,9 @@ The best way to send feedback is to file an issue at [https://github.com/cookiec 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 :) +- 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 @@ -65,56 +71,58 @@ Here's how to set up `cookiecutter` for local development. 1. Fork the `cookiecutter` repo on GitHub. 2. Clone your fork locally: -```bash -git clone git@github.com:your_name_here/cookiecutter.git -``` + ```bash + 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: +3. Install your local copy into a virtualenv. + Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development: -```bash - -cd cookiecutter/ -pip install -e . -``` + ```bash + cd cookiecutter/ + pip install -e . + ``` 4. Create a branch for local development: -```bash -git checkout -b name-of-your-bugfix-or-feature -``` + ```bash + 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 lint check: -```bash -pip install tox -tox -``` + ```bash + pip install tox + tox + ``` -Please note that tox runs lint check automatically, since we have a test environment for it. + Please note that tox runs lint check automatically, since we have a test environment for it. -If you feel like running only the lint environment, please use the following command: + If you feel like running only the lint environment, please use the following command: -```bash -make lint -``` + ```bash + make lint + ``` -6. Ensure that your feature or commit is fully covered by tests. Check report after regular tox run. You can also run coverage only report and get html report with statement by statement highlighting: +6. Ensure that your feature or commit is fully covered by tests. Check report after regular tox run. + You can also run coverage only report and get html report with statement by statement highlighting: -```bash -make coverage -``` + ```bash + make coverage + ``` -You report will be placed to `htmlcov` directory. Please do not include this directory to your commits. By default this directory in our `.gitignore` file. + You report will be placed to `htmlcov` directory. Please do not include this directory to your commits. + By default this directory in our `.gitignore` file. 7. Commit your changes and push your branch to GitHub: -```bash -git add . -git commit -m "Your detailed description of your changes." -git push origin name-of-your-bugfix-or-feature -``` + ```bash + git add . + git commit -m "Your detailed description of your changes." + git push origin name-of-your-bugfix-or-feature + ``` 8. Submit a pull request through the GitHub website. @@ -125,34 +133,36 @@ git push origin name-of-your-bugfix-or-feature Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. -2. The pull request should be contained: if it's too big consider splitting it into smaller pull requests. -3. 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.md. +2. The pull request should be contained: + if it's too big consider splitting it into smaller pull requests. +3. 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.md. 4. The pull request must pass all CI/CD jobs before being ready for review. 5. If one CI/CD job is failing for unrelated reasons you may want to create another PR to fix that first. ### Coding Standards -* PEP8 -* Functions over classes except in tests -* Quotes via [http://stackoverflow.com/a/56190/5549](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: - -```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 -``` +- PEP8 +- Functions over classes except in tests +- Quotes via [http://stackoverflow.com/a/56190/5549](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: + + ```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 + ``` ## Testing with tox @@ -186,7 +196,8 @@ To run all tests using various versions of python in virtualenvs defined in tox. tox ``` -This configuration file setup the pytest-cov plugin and it is an additional dependency. It generate a coverage report after the tests. +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 test with specific versions of Python. To do this, the command is: @@ -202,93 +213,97 @@ This will run `py.test` with the `python3.7` and `python3.8` interpreters. Core committers, use this section to: -* Guide your instinct and decisions as a core committer -* Limit the codebase from growing infinitely +- 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 +- 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 +- 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 +- 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 +- 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 +- 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 +- 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 +- 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 +- 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 +- 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 +- 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. +- 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. #### Pull Requests Review Guidelines -- Think carefully about the long-term implications of the change. How will it affect existing projects that are dependent on this? If this is complicated, do we really want to maintain it forever? -- Take the time to get things right, PRs almost always require additional improvements to meet the bar for quality. **Be very strict about quality.** -- When you merge a pull request take care of closing/updating every related issue explaining how they were affected by those changes. Also, remember to add the author to `AUTHORS.md`. +- Think carefully about the long-term implications of the change. + How will it affect existing projects that are dependent on this? + If this is complicated, do we really want to maintain it forever? +- Take the time to get things right, PRs almost always require additional improvements to meet the bar for quality. + **Be very strict about quality.** +- When you merge a pull request take care of closing/updating every related issue explaining how they were affected by those changes. + Also, remember to add the author to `AUTHORS.md`. ### Process: Issues @@ -307,12 +322,12 @@ Due dates are flexible. Core committers can change them as needed. Note that Git How to number milestones: -* Follow semantic versioning. Look at: [http://semver.org](http://semver.org) +- Follow semantic versioning. Look at: [http://semver.org](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. +- If a milestone contains too much, move some to the next milestone. +- Err on the side of more frequent patch releases. ### Process: Your own code changes @@ -321,23 +336,29 @@ 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. +- 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, macOS and Linux. -* 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. Look at [Code of Conduct](CODE_OF_CONDUCT.md). +- Ensure cross-platform compatibility for every change that's accepted. Windows, macOS and Linux. +- 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. + Look at [Code of Conduct](CODE_OF_CONDUCT.md). ### Becoming a Core Committer Contributors may be given core commit privileges. Preference will be given to those with: -1. 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. +1. 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. 2. A coding style that the other core committers find simple, minimal, and clean. 3. Access to resources for cross-platform development and testing. 4. Time to devote to the project regularly. diff --git a/README.md b/README.md index 493f63cc4..38de8e205 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ [![docs](https://readthedocs.org/projects/cookiecutter/badge/?version=latest)](https://readthedocs.org/projects/cookiecutter/?badge=latest) [![Code Quality](https://img.shields.io/scrutinizer/g/cookiecutter/cookiecutter.svg)](https://scrutinizer-ci.com/g/cookiecutter/cookiecutter/?branch=master) -A command-line utility that creates projects from **cookiecutters** (project templates), -e.g. creating a Python package project from a Python package project template. +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: [https://cookiecutter.readthedocs.io](https://cookiecutter.readthedocs.io) - GitHub: [https://github.com/cookiecutter/cookiecutter](https://github.com/cookiecutter/cookiecutter) @@ -149,9 +148,10 @@ You are almost not limited in topics amount, use it! These Cookiecutters are maintained by the cookiecutter team: - [cookiecutter-pypackage](https://github.com/audreyfeldroy/cookiecutter-pypackage): - [@audreyfeldroy's](https://github.com/audreyfeldroy) ultimate Python package project template. + ultimate Python package project template by [@audreyfeldroy's](https://github.com/audreyfeldroy). - [cookiecutter-django](https://github.com/pydanny/cookiecutter-django): - A bleeding edge Django project template with Bootstrap 4, customizable users app, starter templates, working user registration, celery setup, and much more. + a framework for jumpstarting production-ready Django projects quickly. + It is bleeding edge with Bootstrap 5, customizable users app, starter templates, working user registration, celery setup, and much more. - [cookiecutter-pytest-plugin](https://github.com/pytest-dev/cookiecutter-pytest-plugin): Minimal Cookiecutter template for authoring [pytest](https://docs.pytest.org/) plugins that help you to write better programs. @@ -164,8 +164,8 @@ Stuck? Try one of the following: - See the [Troubleshooting](https://cookiecutter.readthedocs.io/en/latest/troubleshooting.html) page. - Ask for help on [Stack Overflow](https://stackoverflow.com/questions/tagged/cookiecutter). -- You are strongly encouraged to - [file an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) about the problem, even if it's just "I can't get it to work on this cookiecutter" with a link to your cookiecutter. +- You are strongly encouraged to [file an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) about the problem. + Do it 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 [Discord](https://discord.gg/9BrxzPKuEW) if you must (but please try one of the other options first, so that others can benefit from the discussion). @@ -177,8 +177,8 @@ Development on Cookiecutter is community-driven: - Connect with other Cookiecutter contributors and users on [Discord](https://discord.gg/9BrxzPKuEW) (note: due to work and other commitments, a core committer might not always be available) -Encouragement is unbelievably motivating. If you want more work done on -Cookiecutter, show support: +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](https://github.com/cookiecutter/cookiecutter). @@ -186,39 +186,35 @@ Cookiecutter, show support: Got criticism or complaints? -- [File an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) - 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](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) - 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." +- [File an issue](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) 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](https://github.com/cookiecutter/cookiecutter/issues?q=is%3Aopen) 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. +- 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? - [@audreyfeldroy](https://github.com/audreyfeldroy) is available for hire for consultation - or custom development. + [@audreyfeldroy](https://github.com/audreyfeldroy) is available for hire for consultation or custom development. ## Support This Project -This project is run by volunteers. Shortly we will be providing means for -organizations and individuals to support the project. +This project is run by volunteers. +Shortly we will be providing means for organizations and individuals to support the project. ## 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](https://www.pypa.io/en/latest/code-of-conduct/). +Everyone interacting in the Cookiecutter project's codebases and documentation is expected to follow the [PyPA Code of Conduct](https://www.pypa.io/en/latest/code-of-conduct/). +This includes, but is not limited to, issue trackers, chat rooms, mailing lists, and other virtual or in real life communication. ## Creator / Leader This project was created and is led by [Audrey Roy Greenfeld](https://github.com/audreyfeldroy). + +She is supported by a team of maintainers. diff --git a/docs/cookiecutter.rst b/docs/cookiecutter.rst index 790a21f7c..16c1c9dc1 100644 --- a/docs/cookiecutter.rst +++ b/docs/cookiecutter.rst @@ -1,8 +1,10 @@ +=== API === -Submodules ----------- + +This is the Cookiecutter modules API documentation. + cookiecutter.cli module ----------------------- @@ -132,7 +134,6 @@ cookiecutter.zipfile module :undoc-members: :show-inheritance: - Module contents --------------- diff --git a/docs/index.rst b/docs/index.rst index 1a391b521..14f5edbcf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,7 +6,7 @@ Cookiecutter: Better Project Templates ====================================== -Cookiecutter creates projects from project templates, e.g. Python package projects. +Cookiecutter creates projects from **cookiecutters** (project templates), e.g. Python package projects from Python package temnplates. Basics ------ diff --git a/docs/installation.rst b/docs/installation.rst index e4084ab81..068c6d9d9 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -22,7 +22,7 @@ Alternatively on macOS, you can use the `homebrew `_ package ma .. code-block:: bash - $ brew install python3 + brew install python3 Adjust your path @@ -73,13 +73,13 @@ At the command line: .. code-block:: bash - $ python3 -m pip install --user cookiecutter + python3 -m pip install --user cookiecutter Or, if you do not have pip: .. code-block:: bash - $ easy_install --user cookiecutter + easy_install --user cookiecutter Though, pip is recommended, easy_install is deprecated. @@ -87,13 +87,13 @@ Or, if you are using conda, first add conda-forge to your channels: .. code-block:: bash - $ conda config --add channels conda-forge + conda config --add channels conda-forge Once the conda-forge channel has been enabled, cookiecutter can be installed with: .. code-block:: bash - $ conda install cookiecutter + conda install cookiecutter Alternate installations ----------------------- @@ -102,13 +102,13 @@ Alternate installations .. code-block:: bash - $ brew install cookiecutter + brew install cookiecutter **Pipx (Linux, OSX and Windows):** .. code-block:: bash - $ pipx install cookiecutter + pipx install cookiecutter Upgrading @@ -130,12 +130,12 @@ Or with pip: .. code-block:: bash - $ python3 -m pip install --upgrade cookiecutter + python3 -m pip install --upgrade cookiecutter Upgrade Cookiecutter either with easy_install (deprecated): .. code-block:: bash - $ easy_install --upgrade cookiecutter + easy_install --upgrade cookiecutter Then you should be good to go. diff --git a/docs/overview.rst b/docs/overview.rst index 6b8e029ee..6a1895614 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -27,9 +27,8 @@ This is a directory structure for a simple cookiecutter:: You must have: -- A `cookiecutter.json` file. -- A `{{ cookiecutter.project_name }}/` directory, where - `project_name` is defined in your `cookiecutter.json`. +- A ``cookiecutter.json`` file. +- A ``{{ cookiecutter.project_name }}/`` directory, where ``project_name`` is defined in your ``cookiecutter.json``. Beyond that, you can have whatever files/directories you want. From 9ee72e1926de5dbe50608dca26ec5e7c9cf26321 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 18:49:30 +0200 Subject: [PATCH 055/202] housekeeping: run pyupgrade to find remaining py27 artifacts --- cookiecutter/cli.py | 10 ++++----- cookiecutter/config.py | 6 ++--- cookiecutter/environment.py | 8 +++---- cookiecutter/exceptions.py | 8 +++---- cookiecutter/extensions.py | 8 +++---- cookiecutter/generate.py | 14 ++++++------ cookiecutter/hooks.py | 6 ++--- cookiecutter/prompt.py | 10 ++++----- cookiecutter/replay.py | 6 ++--- cookiecutter/utils.py | 2 +- cookiecutter/vcs.py | 4 ++-- cookiecutter/zipfile.py | 4 ++-- tests/replay/test_dump.py | 2 +- tests/replay/test_load.py | 2 +- .../hello_extension/hello_extension.py | 6 ++--- .../local_extension/local_extensions/main.py | 4 +--- tests/test_default_extensions.py | 4 ++-- tests/test_generate_file.py | 12 +++++----- tests/test_generate_files.py | 22 +++++++++---------- tests/test_hooks.py | 8 +++---- tests/test_prompt.py | 14 ++++++------ tests/test_read_user_choice.py | 2 +- tests/vcs/test_clone.py | 16 +++++--------- 23 files changed, 83 insertions(+), 95 deletions(-) diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py index 6b3c583ad..a792fa5f5 100644 --- a/cookiecutter/cli.py +++ b/cookiecutter/cli.py @@ -63,9 +63,9 @@ def list_installed_templates(default_config, passed_config_file): os.path.join(cookiecutter_folder, folder, 'cookiecutter.json') ) ] - click.echo('{} installed templates: '.format(len(template_names))) + click.echo(f'{len(template_names)} installed templates: ') for name in template_names: - click.echo(' * {}'.format(name)) + click.echo(f' * {name}') @click.command(context_settings=dict(help_option_names=['-h', '--help'])) @@ -219,11 +219,11 @@ def main( click.echo(e) sys.exit(1) except UndefinedVariableInTemplate as undefined_err: - click.echo('{}'.format(undefined_err.message)) - click.echo('Error message: {}'.format(undefined_err.error.message)) + click.echo(f'{undefined_err.message}') + click.echo(f'Error message: {undefined_err.error.message}') context_str = json.dumps(undefined_err.context, indent=4, sort_keys=True) - click.echo('Context: {}'.format(context_str)) + click.echo(f'Context: {context_str}') sys.exit(1) diff --git a/cookiecutter/config.py b/cookiecutter/config.py index 90483f532..0d0fa8c7e 100644 --- a/cookiecutter/config.py +++ b/cookiecutter/config.py @@ -55,9 +55,7 @@ def merge_configs(default, overwrite): def get_config(config_path): """Retrieve the config from the specified path, returning a config dict.""" if not os.path.exists(config_path): - raise ConfigDoesNotExistException( - 'Config file {} does not exist.'.format(config_path) - ) + raise ConfigDoesNotExistException(f'Config file {config_path} does not exist.') logger.debug('config_path is %s', config_path) with open(config_path, encoding='utf-8') as file_handle: @@ -65,7 +63,7 @@ def get_config(config_path): yaml_dict = yaml.safe_load(file_handle) except yaml.YAMLError as e: raise InvalidConfiguration( - 'Unable to parse YAML file {}.'.format(config_path) + f'Unable to parse YAML file {config_path}.' ) from e config_dict = merge_configs(DEFAULT_CONFIG, yaml_dict) diff --git a/cookiecutter/environment.py b/cookiecutter/environment.py index 2f91e015a..f2804c595 100644 --- a/cookiecutter/environment.py +++ b/cookiecutter/environment.py @@ -4,7 +4,7 @@ from cookiecutter.exceptions import UnknownExtension -class ExtensionLoaderMixin(object): +class ExtensionLoaderMixin: """Mixin providing sane loading of extensions specified in a given context. The context is being extracted from the keyword arguments before calling @@ -32,9 +32,9 @@ def __init__(self, **kwargs): extensions = default_extensions + self._read_extensions(context) try: - super(ExtensionLoaderMixin, self).__init__(extensions=extensions, **kwargs) + super().__init__(extensions=extensions, **kwargs) except ImportError as err: - raise UnknownExtension('Unable to load extension: {}'.format(err)) + raise UnknownExtension(f'Unable to load extension: {err}') def _read_extensions(self, context): """Return list of extensions as str to be passed on to the Jinja2 env. @@ -62,4 +62,4 @@ def __init__(self, **kwargs): Also loading extensions defined in cookiecutter.json's _extensions key. """ - super(StrictEnvironment, self).__init__(undefined=StrictUndefined, **kwargs) + super().__init__(undefined=StrictUndefined, **kwargs) diff --git a/cookiecutter/exceptions.py b/cookiecutter/exceptions.py index 9461aa985..4acf6dc47 100644 --- a/cookiecutter/exceptions.py +++ b/cookiecutter/exceptions.py @@ -124,10 +124,10 @@ def __init__(self, message, error, context): def __str__(self): """Text representation of UndefinedVariableInTemplate.""" return ( - "{self.message}. " - "Error message: {self.error.message}. " - "Context: {self.context}" - ).format(**locals()) + f"{self.message}. " + f"Error message: {self.error.message}. " + f"Context: {self.context}" + ) class UnknownExtension(CookiecutterException): diff --git a/cookiecutter/extensions.py b/cookiecutter/extensions.py index e3a42b244..6a3161aba 100644 --- a/cookiecutter/extensions.py +++ b/cookiecutter/extensions.py @@ -13,7 +13,7 @@ class JsonifyExtension(Extension): def __init__(self, environment): """Initialize the extension with the given environment.""" - super(JsonifyExtension, self).__init__(environment) + super().__init__(environment) def jsonify(obj): return json.dumps(obj, sort_keys=True, indent=4) @@ -26,7 +26,7 @@ class RandomStringExtension(Extension): def __init__(self, environment): """Jinja2 Extension Constructor.""" - super(RandomStringExtension, self).__init__(environment) + super().__init__(environment) def random_ascii_string(length, punctuation=False): if punctuation: @@ -43,7 +43,7 @@ class SlugifyExtension(Extension): def __init__(self, environment): """Jinja2 Extension constructor.""" - super(SlugifyExtension, self).__init__(environment) + super().__init__(environment) def slugify(value, **kwargs): """Slugifies the value.""" @@ -57,7 +57,7 @@ class UUIDExtension(Extension): def __init__(self, environment): """Jinja2 Extension constructor.""" - super(UUIDExtension, self).__init__(environment) + super().__init__(environment) def uuid4(): """Generate UUID4.""" diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index 7dbd9867b..7bdce5a8b 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -96,8 +96,8 @@ def generate_context( 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) + 'JSON decoding error while loading "{}". Decoding' + ' error details: "{}"'.format(full_fpath, json_exc_message) ) raise ContextDecodingException(our_exc_message) @@ -180,7 +180,7 @@ def generate_file(project_dir, infile, context, env, skip_if_file_exists=False): # Detect original file newline to output the rendered file # note: newline='' ensures newlines are not converted - with open(infile, 'r', encoding='utf-8', newline='') as rd: + with open(infile, encoding='utf-8', newline='') as rd: rd.readline() # Read the first line to load 'newlines' value # Use `_new_lines` overwrite from context, if configured. @@ -219,7 +219,7 @@ def render_and_create_dir( 'Output directory %s already exists, overwriting it', dir_to_create ) else: - msg = 'Error: "{}" directory already exists'.format(dir_to_create) + msg = f'Error: "{dir_to_create}" directory already exists' raise OutputDirExistsException(msg) else: make_sure_path_exists(dir_to_create) @@ -292,7 +292,7 @@ def generate_files( unrendered_dir, context, output_dir, env, overwrite_if_exists ) except UndefinedError as err: - msg = "Unable to create project directory '{}'".format(unrendered_dir) + msg = f"Unable to create project directory '{unrendered_dir}'" raise UndefinedVariableInTemplate(msg, err, context) # We want the Jinja path and the OS paths to match. Consequently, we'll: @@ -354,7 +354,7 @@ def generate_files( if delete_project_on_failure: rmtree(project_dir) _dir = os.path.relpath(unrendered_dir, output_dir) - msg = "Unable to create directory '{}'".format(_dir) + msg = f"Unable to create directory '{_dir}'" raise UndefinedVariableInTemplate(msg, err, context) for f in files: @@ -376,7 +376,7 @@ def generate_files( except UndefinedError as err: if delete_project_on_failure: rmtree(project_dir) - msg = "Unable to create file '{}'".format(infile) + msg = f"Unable to create file '{infile}'" raise UndefinedVariableInTemplate(msg, err, context) if accept_hooks: diff --git a/cookiecutter/hooks.py b/cookiecutter/hooks.py index b6a31a1e0..763287c58 100644 --- a/cookiecutter/hooks.py +++ b/cookiecutter/hooks.py @@ -83,14 +83,14 @@ def run_script(script_path, cwd='.'): exit_status = proc.wait() if exit_status != EXIT_SUCCESS: raise FailedHookException( - 'Hook script failed (exit status: {})'.format(exit_status) + f'Hook script failed (exit status: {exit_status})' ) except OSError as os_error: if os_error.errno == errno.ENOEXEC: raise FailedHookException( 'Hook script failed, might be an empty file or missing a shebang' ) - raise FailedHookException('Hook script failed (error: {})'.format(os_error)) + raise FailedHookException(f'Hook script failed (error: {os_error})') def run_script_with_context(script_path, cwd, context): @@ -102,7 +102,7 @@ def run_script_with_context(script_path, cwd, context): """ _, extension = os.path.splitext(script_path) - with open(script_path, 'r', encoding='utf-8') as file: + with open(script_path, encoding='utf-8') as file: contents = file.read() with tempfile.NamedTemporaryFile(delete=False, mode='wb', suffix=extension) as temp: diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index 4b8b2fbe6..f06cdc3c0 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -58,16 +58,14 @@ def read_user_choice(var_name, options): if not options: raise ValueError - choice_map = OrderedDict( - ('{}'.format(i), value) for i, value in enumerate(options, 1) - ) + choice_map = OrderedDict((f'{i}', value) for i, value in enumerate(options, 1)) choices = choice_map.keys() default = '1' choice_lines = ['{} - {}'.format(*c) for c in choice_map.items()] prompt = '\n'.join( ( - 'Select {}:'.format(var_name), + f'Select {var_name}:', '\n'.join(choice_lines), 'Choose from {}'.format(', '.join(choices)), ) @@ -213,7 +211,7 @@ def prompt_for_config(context, no_input=False): cookiecutter_dict[key] = val except UndefinedError as err: - msg = "Unable to render variable '{}'".format(key) + msg = f"Unable to render variable '{key}'" raise UndefinedVariableInTemplate(msg, err, context) # Second pass; handle the dictionaries. @@ -232,7 +230,7 @@ def prompt_for_config(context, no_input=False): cookiecutter_dict[key] = val except UndefinedError as err: - msg = "Unable to render variable '{}'".format(key) + msg = f"Unable to render variable '{key}'" raise UndefinedVariableInTemplate(msg, err, context) return cookiecutter_dict diff --git a/cookiecutter/replay.py b/cookiecutter/replay.py index 504a6a32c..9730e84da 100644 --- a/cookiecutter/replay.py +++ b/cookiecutter/replay.py @@ -12,14 +12,14 @@ def get_file_name(replay_dir, template_name): """Get the name of file.""" suffix = '.json' if not template_name.endswith('.json') else '' - file_name = '{}{}'.format(template_name, suffix) + file_name = f'{template_name}{suffix}' return os.path.join(replay_dir, file_name) def dump(replay_dir, template_name, context): """Write json data to file.""" if not make_sure_path_exists(replay_dir): - raise IOError('Unable to create replay dir at {}'.format(replay_dir)) + raise OSError(f'Unable to create replay dir at {replay_dir}') if not isinstance(template_name, str): raise TypeError('Template name is required to be of type str') @@ -43,7 +43,7 @@ def load(replay_dir, template_name): replay_file = get_file_name(replay_dir, template_name) - with open(replay_file, 'r') as infile: + with open(replay_file) as infile: context = json.load(infile) if 'cookiecutter' not in context: diff --git a/cookiecutter/utils.py b/cookiecutter/utils.py index ef533171a..4750a2663 100644 --- a/cookiecutter/utils.py +++ b/cookiecutter/utils.py @@ -113,7 +113,7 @@ def simple_filter(filter_function): class SimpleFilterExtension(Extension): def __init__(self, environment): - super(SimpleFilterExtension, self).__init__(environment) + super().__init__(environment) environment.filters[filter_function.__name__] = filter_function SimpleFilterExtension.__name__ = filter_function.__name__ diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py index d7f7b5ba5..08cb2eb0c 100644 --- a/cookiecutter/vcs.py +++ b/cookiecutter/vcs.py @@ -73,7 +73,7 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): # 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) + msg = f"'{repo_type}' is not installed." raise VCSNotInstalled(msg) repo_url = repo_url.rstrip('/') @@ -83,7 +83,7 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): repo_dir = os.path.normpath(os.path.join(clone_to_dir, repo_name)) if repo_type == 'hg': repo_dir = os.path.normpath(os.path.join(clone_to_dir, repo_name)) - logger.debug('repo_dir is {0}'.format(repo_dir)) + logger.debug(f'repo_dir is {repo_dir}') if os.path.isdir(repo_dir): clone = prompt_and_delete(repo_dir, no_input=no_input) diff --git a/cookiecutter/zipfile.py b/cookiecutter/zipfile.py index 24925c7fc..7395ce61b 100644 --- a/cookiecutter/zipfile.py +++ b/cookiecutter/zipfile.py @@ -55,7 +55,7 @@ def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None): zip_file = ZipFile(zip_path) if len(zip_file.namelist()) == 0: - raise InvalidZipRepository('Zip repository {} is empty'.format(zip_uri)) + raise InvalidZipRepository(f'Zip repository {zip_uri} is empty') # The first record in the zipfile should be the directory entry for # the archive. If it isn't a directory, there's a problem. @@ -106,7 +106,7 @@ def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None): except BadZipFile: raise InvalidZipRepository( - 'Zip repository {} is not a valid zip archive:'.format(zip_uri) + f'Zip repository {zip_uri} is not a valid zip archive:' ) return unzip_path diff --git a/tests/replay/test_dump.py b/tests/replay/test_dump.py index ec8010b3e..c757321b1 100644 --- a/tests/replay/test_dump.py +++ b/tests/replay/test_dump.py @@ -16,7 +16,7 @@ def template_name(): @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) + file_name = f'{template_name}.json' return os.path.join(replay_test_dir, file_name) diff --git a/tests/replay/test_load.py b/tests/replay/test_load.py index a64a285e1..c8bc453e0 100644 --- a/tests/replay/test_load.py +++ b/tests/replay/test_load.py @@ -16,7 +16,7 @@ def template_name(): @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) + file_name = f'{template_name}.json' return os.path.join(replay_test_dir, file_name) diff --git a/tests/test-extensions/hello_extension/hello_extension.py b/tests/test-extensions/hello_extension/hello_extension.py index f54b6efdd..07f3753b9 100644 --- a/tests/test-extensions/hello_extension/hello_extension.py +++ b/tests/test-extensions/hello_extension/hello_extension.py @@ -6,15 +6,15 @@ class HelloExtension(Extension): """Simple jinja2 extension for cookiecutter test purposes.""" - tags = set(['hello']) + tags = {'hello'} def __init__(self, environment): """Hello Extension Constructor.""" - super(HelloExtension, self).__init__(environment) + super().__init__(environment) def _hello(self, name): """Do actual tag replace when invoked by parser.""" - return 'Hello {name}!'.format(name=name) + return f'Hello {name}!' def parse(self, parser): """Work when something match `tags` variable.""" diff --git a/tests/test-extensions/local_extension/local_extensions/main.py b/tests/test-extensions/local_extension/local_extensions/main.py index 53f6f8f95..b18a25c91 100644 --- a/tests/test-extensions/local_extension/local_extensions/main.py +++ b/tests/test-extensions/local_extension/local_extensions/main.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Provides custom extension, exposing a ``foobar`` filter.""" from jinja2.ext import Extension @@ -11,7 +9,7 @@ class FoobarExtension(Extension): def __init__(self, environment): """Foobar Extension Constructor.""" - super(FoobarExtension, self).__init__(environment) + super().__init__(environment) environment.filters['foobar'] = lambda v: v * 2 diff --git a/tests/test_default_extensions.py b/tests/test_default_extensions.py index e73ef9c1b..540138d31 100644 --- a/tests/test_default_extensions.py +++ b/tests/test_default_extensions.py @@ -25,7 +25,7 @@ def test_jinja2_time_extension(tmp_path): changelog_file = os.path.join(project_dir, 'HISTORY.rst') assert os.path.isfile(changelog_file) - with open(changelog_file, 'r', encoding='utf-8') as f: + with open(changelog_file, encoding='utf-8') as f: changelog_lines = f.readlines() expected_lines = [ @@ -57,7 +57,7 @@ def test_jinja2_uuid_extension(tmp_path): changelog_file = os.path.join(project_dir, 'id') assert os.path.isfile(changelog_file) - with open(changelog_file, 'r', encoding='utf-8') as f: + with open(changelog_file, encoding='utf-8') as f: changelog_lines = f.readlines() uuid.UUID(changelog_lines[0], version=4) diff --git a/tests/test_generate_file.py b/tests/test_generate_file.py index a393a2ef8..18c811eea 100644 --- a/tests/test_generate_file.py +++ b/tests/test_generate_file.py @@ -45,7 +45,7 @@ def test_generate_file(env): env=env, ) assert os.path.isfile('tests/files/cheese.txt') - with open('tests/files/cheese.txt', 'rt') as f: + with open('tests/files/cheese.txt') as f: generated_text = f.read() assert generated_text == 'Testing cheese' @@ -58,7 +58,7 @@ def test_generate_file_jsonify_filter(env): project_dir=".", infile=infile, context={'cookiecutter': data}, env=env ) assert os.path.isfile('tests/files/cheese.txt') - with open('tests/files/cheese.txt', 'rt') as f: + with open('tests/files/cheese.txt') as f: generated_text = f.read() assert json.loads(generated_text) == data @@ -72,7 +72,7 @@ def test_generate_file_random_ascii_string(env, length, punctuation): context = {"cookiecutter": data, "length": length, "punctuation": punctuation} generate.generate_file(project_dir=".", infile=infile, context=context, env=env) assert os.path.isfile('tests/files/cheese.txt') - with open('tests/files/cheese.txt', 'rt') as f: + with open('tests/files/cheese.txt') as f: generated_text = f.read() assert len(generated_text) == length @@ -92,7 +92,7 @@ def test_generate_file_with_true_condition(env): env=env, ) assert os.path.isfile('tests/files/cheese.txt') - with open('tests/files/cheese.txt', 'rt') as f: + with open('tests/files/cheese.txt') as f: generated_text = f.read() assert generated_text == 'Testing that generate_file was y' @@ -148,7 +148,7 @@ def test_generate_file_does_not_translate_lf_newlines_to_crlf(env, tmp_path): # this generated file should have a LF line ending gf = 'tests/files/cheese_lf_newlines.txt' - with open(gf, 'r', encoding='utf-8', newline='') as f: + with open(gf, encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\n' assert f.newlines == '\n' @@ -166,7 +166,7 @@ def test_generate_file_does_not_translate_crlf_newlines_to_lf(env): # this generated file should have a CRLF line ending gf = 'tests/files/cheese_crlf_newlines.txt' - with open(gf, 'r', encoding='utf-8', newline='') as f: + with open(gf, encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\r\n' assert f.newlines == '\r\n' diff --git a/tests/test_generate_files.py b/tests/test_generate_files.py index 4e21c7053..4d6ef1113 100644 --- a/tests/test_generate_files.py +++ b/tests/test_generate_files.py @@ -44,7 +44,7 @@ def test_generate_files(tmp_path): assert simple_file.exists() assert simple_file.is_file() - simple_text = open(simple_file, 'rt', encoding='utf-8').read() + simple_text = open(simple_file, encoding='utf-8').read() assert simple_text == 'I eat pizzä' @@ -60,7 +60,7 @@ def test_generate_files_with_linux_newline(tmp_path): assert newline_file.is_file() assert newline_file.exists() - with open(newline_file, 'r', encoding='utf-8', newline='') as f: + with open(newline_file, encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\n' assert f.newlines == '\n' @@ -84,7 +84,7 @@ def test_generate_files_with_jinja2_environment(tmp_path): assert conditions_file.exists() simple_text = conditions_file.open('rt', encoding='utf-8').read() - assert simple_text == u'I eat pizzä\n' + assert simple_text == 'I eat pizzä\n' def test_generate_files_with_trailing_newline_forced_to_linux_by_context(tmp_path): @@ -100,7 +100,7 @@ def test_generate_files_with_trailing_newline_forced_to_linux_by_context(tmp_pat assert newline_file.is_file() assert newline_file.exists() - with open(newline_file, 'r', encoding='utf-8', newline='') as f: + with open(newline_file, encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\r\n' assert f.newlines == '\r\n' @@ -118,7 +118,7 @@ def test_generate_files_with_windows_newline(tmp_path): assert newline_file.is_file() assert newline_file.exists() - with open(newline_file, 'r', encoding='utf-8', newline='') as f: + with open(newline_file, encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\r\n' assert f.newlines == '\r\n' @@ -136,7 +136,7 @@ def test_generate_files_with_windows_newline_forced_to_linux_by_context(tmp_path assert newline_file.is_file() assert newline_file.exists() - with open(newline_file, 'r', encoding='utf-8', newline='') as f: + with open(newline_file, encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\n' @@ -257,7 +257,7 @@ def test_generate_files_with_overwrite_if_exists_with_skip_if_file_exists(tmp_pa assert Path(simple_with_new_line_file).is_file() assert Path(simple_with_new_line_file).exists() - simple_text = open(simple_file, 'rt', encoding='utf-8').read() + simple_text = open(simple_file, encoding='utf-8').read() assert simple_text == 'temp' @@ -283,7 +283,7 @@ def test_generate_files_with_skip_if_file_exists(tmp_path): assert not Path(simple_with_new_line_file).is_file() assert not Path(simple_with_new_line_file).exists() - simple_text = open(simple_file, 'rt', encoding='utf-8').read() + simple_text = open(simple_file, encoding='utf-8').read() assert simple_text == 'temp' @@ -308,7 +308,7 @@ def test_generate_files_with_overwrite_if_exists(tmp_path): assert Path(simple_with_new_line_file).is_file() assert Path(simple_with_new_line_file).exists() - simple_text = open(simple_file, 'rt', encoding='utf-8').read() + simple_text = open(simple_file, encoding='utf-8').read() assert simple_text == 'I eat pizzä' @@ -382,7 +382,7 @@ def test_raise_undefined_variable_dir_name(output_dir, undefined_context): error = err.value directory = Path('testproject', '{{cookiecutter.foobar}}') - msg = "Unable to create directory '{}'".format(directory) + msg = f"Unable to create directory '{directory}'" assert msg == error.message assert error.context == undefined_context @@ -407,7 +407,7 @@ def test_raise_undefined_variable_dir_name_existing_project( error = err.value directory = Path('testproject', '{{cookiecutter.foobar}}') - msg = "Unable to create directory '{}'".format(directory) + msg = f"Unable to create directory '{directory}'" assert msg == error.message assert error.context == undefined_context diff --git a/tests/test_hooks.py b/tests/test_hooks.py index d8b55dff2..d214e714b 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -71,7 +71,7 @@ def make_test_repo(name, multiple_hooks=False): return post -class TestFindHooks(object): +class TestFindHooks: """Class to unite find hooks related tests in one place.""" repo_path = 'tests/test-hooks' @@ -91,7 +91,7 @@ def test_find_hook(self): actual_hook_path = hooks.find_hook('pre_gen_project') assert expected_pre == actual_hook_path[0] - expected_post = os.path.abspath('hooks/{}'.format(self.post_hook)) + expected_post = os.path.abspath(f'hooks/{self.post_hook}') actual_hook_path = hooks.find_hook('post_gen_project') assert expected_post == actual_hook_path[0] @@ -111,7 +111,7 @@ def test_hook_not_found(self): assert hooks.find_hook('unknown_hook') is None -class TestExternalHooks(object): +class TestExternalHooks: """Class to unite tests for hooks with different project paths.""" repo_path = os.path.abspath('tests/test-hooks/') @@ -154,7 +154,7 @@ def test_run_failing_script(self, mocker): with pytest.raises(exceptions.FailedHookException) as excinfo: hooks.run_script(os.path.join(self.hooks_path, self.post_hook)) - assert 'Hook script failed (error: {})'.format(err) in str(excinfo.value) + assert f'Hook script failed (error: {err})' in str(excinfo.value) def test_run_failing_script_enoexec(self, mocker): """Test correct exception raise if run_script fails.""" diff --git a/tests/test_prompt.py b/tests/test_prompt.py index 8187cb283..037591dd4 100644 --- a/tests/test_prompt.py +++ b/tests/test_prompt.py @@ -66,7 +66,7 @@ def test_convert_to_str_complex_variables(self, raw_var, rendered_var): assert result == rendered_var -class TestPrompt(object): +class TestPrompt: """Class to unite user prompt related tests.""" @pytest.mark.parametrize( @@ -210,11 +210,11 @@ def test_should_render_private_variables_with_two_underscores(self): [ ('foo', 'Hello world'), ('bar', 123), - ('rendered_foo', u'{{ cookiecutter.foo|lower }}'), + ('rendered_foo', '{{ cookiecutter.foo|lower }}'), ('rendered_bar', 123), - ('_hidden_foo', u'{{ cookiecutter.foo|lower }}'), + ('_hidden_foo', '{{ cookiecutter.foo|lower }}'), ('_hidden_bar', 123), - ('__rendered_hidden_foo', u'{{ cookiecutter.foo|lower }}'), + ('__rendered_hidden_foo', '{{ cookiecutter.foo|lower }}'), ('__rendered_hidden_bar', 123), ] ) @@ -226,7 +226,7 @@ def test_should_render_private_variables_with_two_underscores(self): ('bar', '123'), ('rendered_foo', 'hello world'), ('rendered_bar', '123'), - ('_hidden_foo', u'{{ cookiecutter.foo|lower }}'), + ('_hidden_foo', '{{ cookiecutter.foo|lower }}'), ('_hidden_bar', 123), ('__rendered_hidden_foo', 'hello world'), ('__rendered_hidden_bar', '123'), @@ -252,7 +252,7 @@ def test_should_not_render_private_variables(self): assert cookiecutter_dict == context['cookiecutter'] -class TestReadUserChoice(object): +class TestReadUserChoice: """Class to unite choices prompt related tests.""" def test_should_invoke_read_user_choice(self, mocker): @@ -332,7 +332,7 @@ def test_should_render_choices(self, mocker): assert cookiecutter_dict == expected -class TestPromptChoiceForConfig(object): +class TestPromptChoiceForConfig: """Class to unite choices prompt related tests with config test.""" @pytest.fixture diff --git a/tests/test_read_user_choice.py b/tests/test_read_user_choice.py index ef9ae62f4..f3573593c 100644 --- a/tests/test_read_user_choice.py +++ b/tests/test_read_user_choice.py @@ -24,7 +24,7 @@ def test_click_invocation(mocker, user_choice, expected_value): choice.return_value = click.Choice(OPTIONS) prompt = mocker.patch('click.prompt') - prompt.return_value = '{}'.format(user_choice) + prompt.return_value = f'{user_choice}' assert read_user_choice('varname', OPTIONS) == expected_value diff --git a/tests/vcs/test_clone.py b/tests/vcs/test_clone.py index cd4ac13d7..9fc3b24fa 100644 --- a/tests/vcs/test_clone.py +++ b/tests/vcs/test_clone.py @@ -130,10 +130,8 @@ def test_clone_should_invoke_vcs_command( @pytest.mark.parametrize( 'error_message', [ - ( - "fatal: repository 'https://github.com/hackebro/cookiedozer' not found" - ).encode('utf-8'), - 'hg: abort: HTTP Error 404: Not Found'.encode('utf-8'), + (b"fatal: repository 'https://github.com/hackebro/cookiedozer' not found"), + b'hg: abort: HTTP Error 404: Not Found', ], ) def test_clone_handles_repo_typo(mocker, clone_dir, error_message): @@ -160,10 +158,8 @@ def test_clone_handles_repo_typo(mocker, clone_dir, error_message): @pytest.mark.parametrize( 'error_message', [ - ( - "error: pathspec 'unknown_branch' did not match any file(s) known to git" - ).encode('utf-8'), - "hg: abort: unknown revision 'unknown_branch'!".encode('utf-8'), + b"error: pathspec 'unknown_branch' did not match any file(s) known to git", + b"hg: abort: unknown revision 'unknown_branch'!", ], ) def test_clone_handles_branch_typo(mocker, clone_dir, error_message): @@ -196,9 +192,7 @@ def test_clone_unknown_subprocess_error(mocker, clone_dir): 'cookiecutter.vcs.subprocess.check_output', autospec=True, side_effect=[ - subprocess.CalledProcessError( - -1, 'cmd', output='Something went wrong'.encode('utf-8') - ) + subprocess.CalledProcessError(-1, 'cmd', output=b'Something went wrong') ], ) From f01124001b21e8f19a0b06d5518b70515c3ef332 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 20:24:02 +0200 Subject: [PATCH 056/202] Documentation overhaul (3) --- docs/advanced/calling_from_python.rst | 10 ++-- docs/advanced/choice_variables.rst | 28 ++++++++---- docs/advanced/copy_without_render.rst | 14 ++++-- docs/advanced/dict_variables.rst | 24 +++++----- docs/advanced/directories.rst | 15 +++--- docs/advanced/hooks.rst | 51 +++++++++------------ docs/advanced/injecting_context.rst | 33 ++++++++++---- docs/advanced/local_extensions.rst | 4 +- docs/advanced/new_line_characters.rst | 23 ++++++---- docs/advanced/private_variables.rst | 17 +++++-- docs/advanced/replay.rst | 20 +++++--- docs/advanced/suppressing_prompts.rst | 17 ++++--- docs/advanced/template_extensions.rst | 35 ++++++-------- docs/advanced/templates_in_context.rst | 28 ++++++------ docs/advanced/user_config.rst | 26 +++++++---- docs/tutorials/tutorial1.rst | 63 ++++++++++++-------------- docs/tutorials/tutorial2.rst | 13 +++--- 17 files changed, 235 insertions(+), 186 deletions(-) diff --git a/docs/advanced/calling_from_python.rst b/docs/advanced/calling_from_python.rst index 4c7f2e9ad..b497b6bf0 100644 --- a/docs/advanced/calling_from_python.rst +++ b/docs/advanced/calling_from_python.rst @@ -3,7 +3,9 @@ Calling Cookiecutter Functions From Python ------------------------------------------ -You can use Cookiecutter from Python:: +You can use Cookiecutter from Python: + +.. code-block:: python from cookiecutter.main import cookiecutter @@ -13,6 +15,6 @@ You can use Cookiecutter from Python:: # 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`. +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`. + +See the :ref:`API Reference ` for more details. diff --git a/docs/advanced/choice_variables.rst b/docs/advanced/choice_variables.rst index ccb35d4bf..706d53947 100644 --- a/docs/advanced/choice_variables.rst +++ b/docs/advanced/choice_variables.rst @@ -1,17 +1,21 @@ .. _choice-variables: -Choice Variables (1.1+) ------------------------ +Choice Variables +---------------- -Choice variables provide different choices when creating a project. Depending on a user's choice -the template renders things differently. +*New in Cookiecutter 1.1* + +Choice variables provide different choices when creating a project. +Depending on a user's choice the template renders things differently. Basic Usage ~~~~~~~~~~~ Choice variables are regular key / value pairs, but with the value being a list of strings. -For example, if you provide the following choice variable in your ``cookiecutter.json``:: +For example, if you provide the following choice variable in your ``cookiecutter.json``: + +.. code-block:: JSON { "license": ["MIT", "BSD-3", "GNU GPL v3.0", "Apache Software License 2.0"] @@ -28,8 +32,9 @@ you'd get the following choices when running Cookiecutter:: Depending on an user's choice, a different license is rendered by Cookiecutter. -The above ``license`` choice variable creates ``cookiecutter.license``, which -can be used like this:: +The above ``license`` choice variable creates ``cookiecutter.license``, which can be used like this: + +.. code-block:: html+jinja {%- if cookiecutter.license == "MIT" -%} # Possible license content here @@ -41,7 +46,10 @@ can be used like this:: Cookiecutter is using `Jinja2's if conditional expression `_ to determine the correct license. -The created choice variable is still a regular Cookiecutter variable and can be used like this:: +The created choice variable is still a regular Cookiecutter variable and can be used like this: + +.. code-block:: html+jinja + License ------- @@ -53,7 +61,9 @@ Overwriting Default Choice Values Choice Variables are overwritable using a :ref:`user-config` file. -For example, a choice variable can be created in ``cookiecutter.json`` by using a list as value:: +For example, a choice variable can be created in ``cookiecutter.json`` by using a list as value: + +.. code-block:: JSON { "license": ["MIT", "BSD-3", "GNU GPL v3.0", "Apache Software License 2.0"] diff --git a/docs/advanced/copy_without_render.rst b/docs/advanced/copy_without_render.rst index 2cdb68084..5e4bc008d 100644 --- a/docs/advanced/copy_without_render.rst +++ b/docs/advanced/copy_without_render.rst @@ -5,7 +5,10 @@ Copy without Render *New in Cookiecutter 1.1* -To avoid rendering directories and files of a cookiecutter, 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:: +To avoid rendering directories and files of a cookiecutter, 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: + +.. code-block:: JSON { "project_slug": "sample", @@ -16,7 +19,12 @@ To avoid rendering directories and files of a cookiecutter, the `_copy_without_r ] } -**Note**: Only the content of the files will be copied without being rendered. The paths are subject to rendering. This allows you to write:: +**Note**: +Only the content of the files will be copied without being rendered. +The paths are subject to rendering. +This allows you to write: + +.. code-block:: JSON { "project_slug": "sample", @@ -25,4 +33,4 @@ To avoid rendering directories and files of a cookiecutter, the `_copy_without_r ] } -In this example, `{{cookiecutter.repo_name}}` will be rendered as expected but the html file content will be copied without rendering. +In this example, ``{{cookiecutter.repo_name}}`` will be rendered as expected but the html file content will be copied without rendering. diff --git a/docs/advanced/dict_variables.rst b/docs/advanced/dict_variables.rst index 1db841fa1..6142f4bd8 100644 --- a/docs/advanced/dict_variables.rst +++ b/docs/advanced/dict_variables.rst @@ -1,20 +1,21 @@ .. _dict-variables: -Dictionary Variables (1.5+) ---------------------------- +Dictionary Variables +-------------------- -Dictionary variables provide a way to define deep structured information when -rendering a template. +*New in Cookiecutter 1.5* + +Dictionary variables provide a way to define deep structured information when rendering a template. Basic Usage ~~~~~~~~~~~ -Dictionary variables are, as the name suggests, dictionaries of key-value -pairs. The dictionary values can, themselves, be other dictionaries and lists -- the data structure can be as deep as you need. +Dictionary variables are, as the name suggests, dictionaries of key-value pairs. +The dictionary values can, themselves, be other dictionaries and lists - the data structure can be as deep as you need. + +For example, you could provide the following dictionary variable in your ``cookiecutter.json``: -For example, you could provide the following dictionary variable in your -``cookiecutter.json``:: +.. code-block:: json { "project_slug": "new_project", @@ -38,8 +39,9 @@ For example, you could provide the following dictionary variable in your } -The above ``file_type`` dictionary variable creates -``cookiecutter.file_types``, which can be used like this:: +The above ``file_type`` dictionary variable creates ``cookiecutter.file_types``, which can be used like this: + +.. code-block:: html+jinja {% for extension, details in cookiecutter.file_types|dictsort %}
diff --git a/docs/advanced/directories.rst b/docs/advanced/directories.rst index cbec8e4ae..ba0596ded 100644 --- a/docs/advanced/directories.rst +++ b/docs/advanced/directories.rst @@ -1,14 +1,13 @@ .. _directories: -Organizing cookiecutters in directories (1.7+) ---------------------------------------------------- +Organizing cookiecutters in directories +--------------------------------------- *New in Cookiecutter 1.7* -Cookiecutter introduces the ability to organize several templates in one -repository or zip file, separating them by directories. This allows using -symlinks for general files. Here's an example repository demonstrating -this feature:: +Cookiecutter introduces the ability to organize several templates in one repository or zip file, separating them by directories. +This allows using symlinks for general files. +Here's an example repository demonstrating this feature:: https://github.com/user/repo-name.git ├── directory1-name/ @@ -18,6 +17,8 @@ this feature:: ├── {{cookiecutter.project_slug}}/ └── cookiecutter.json -To activate one of templates within a subdirectory, use the ``--directory`` option:: +To activate one of templates within a subdirectory, use the ``--directory`` option: + +.. code-block:: bash cookiecutter https://github.com/user/repo-name.git --directory="directory1-name" diff --git a/docs/advanced/hooks.rst b/docs/advanced/hooks.rst index c659fd407..d08cf9e75 100644 --- a/docs/advanced/hooks.rst +++ b/docs/advanced/hooks.rst @@ -1,12 +1,13 @@ .. _user-hooks: -Using Pre/Post-Generate Hooks (0.7.0+) -====================================== +Using Pre/Post-Generate Hooks +============================= -You can have Python or Shell scripts that run before and/or after your project -is generated. +*New in cookiecutter 0.7* -Put them in `hooks/` like this:: +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.project_slug}}/ @@ -24,13 +25,11 @@ Shell scripts work similarly:: │ └── 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. +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. +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. Writing hooks ------------- @@ -40,25 +39,21 @@ Here are some details on how to write pre/post-generate hook scripts. Exit with an appropriate status ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -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. +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. Current working directory ^^^^^^^^^^^^^^^^^^^^^^^^^ -When the hook scripts script are run, their current working directory is the -root of the generated project. This makes it easy for a post-generate hook to -find generated files using relative paths. +When the hook scripts script are run, their current working directory is the root of the generated project. +This makes it easy for a post-generate hook to find generated files using relative paths. Template variables are rendered in the script ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Just like your project template, Cookiecutter also renders Jinja template -syntax in your scripts. This lets you incorporate Jinja template variables in -your scripts. For example, this line of Python sets ``module_name`` to the -value of the ``cookiecutter.module_name`` template variable: +Just like your project template, Cookiecutter also renders Jinja template syntax in your scripts. +This lets you incorporate Jinja template variables in your scripts. +For example, this line of Python sets ``module_name`` to the value of the ``cookiecutter.module_name`` template variable: .. code-block:: python @@ -67,9 +62,7 @@ value of the ``cookiecutter.module_name`` template variable: Example: Validating template variables -------------------------------------- -Here is an example of a pre-generate hook script, defined at -``hooks/pre_gen_project.py``, that validates a template variable before generating the -project: +Here is an example of a pre-generate hook script, defined at ``hooks/pre_gen_project.py``, that validates a template variable before generating the project: .. code-block:: python @@ -90,12 +83,10 @@ project: Example: Conditional files / directories ---------------------------------------- -Here is an example of a post-generate hook script, defined at -``hooks/post_gen_project.py``, on how to achieve conditional control of files and -directories after generating the project. +Here is an example of a post-generate hook script. +The file ``hooks/post_gen_project.py`` shows how to achieve conditional control of files and directories after generating the project. -The script ensures that the directory structure is as expected by -removing unwanted files and directories: +The script ensures that the directory structure is as expected by removing unwanted files and directories: .. code-block:: python diff --git a/docs/advanced/injecting_context.rst b/docs/advanced/injecting_context.rst index 32927c07f..5561b3d83 100644 --- a/docs/advanced/injecting_context.rst +++ b/docs/advanced/injecting_context.rst @@ -3,17 +3,30 @@ Injecting Extra Context ----------------------- -You can specify an `extra_context` dictionary that will override values from `cookiecutter.json` or `.cookiecutterrc`:: +You can specify an ``extra_context`` dictionary that will override values from ``cookiecutter.json`` or ``.cookiecutterrc``: - cookiecutter('cookiecutter-pypackage/', - extra_context={'project_name': 'TheGreatest'}) +.. code-block:: python + + cookiecutter( + 'cookiecutter-pypackage/', + extra_context={'project_name': 'TheGreatest'}, + ) + +This works as command-line parameters as well: + +.. code-block:: bash + + cookiecutter --no-input cookiecutter-pypackage/ project_name=TheGreatest + +You will also need to add these keys to the ``cookiecutter.json`` or ``.cookiecutterrc``. -You will also need to add these keys to the `cookiecutter.json` or `.cookiecutterrc`. Example: Injecting a Timestamp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you have ``cookiecutter.json`` that has the following keys:: +If you have ``cookiecutter.json`` that has the following keys: + +.. code-block:: JSON { "timestamp": "{{ cookiecutter.timestamp }}" @@ -21,7 +34,9 @@ If you have ``cookiecutter.json`` that has the following keys:: This Python script will dynamically inject a timestamp value as the project is -generated:: +generated: + +.. code-block:: python from cookiecutter.main import cookiecutter @@ -34,6 +49,6 @@ generated:: 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. +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. diff --git a/docs/advanced/local_extensions.rst b/docs/advanced/local_extensions.rst index a90b04d7d..9a3d4b940 100644 --- a/docs/advanced/local_extensions.rst +++ b/docs/advanced/local_extensions.rst @@ -35,8 +35,8 @@ It will contain a ``main.py`` file, containing the following (for instance): This will register the ``foobar`` filter for the template. -For many cases, this will be unneccessarily complicated. It's likely that we'd only want to register a single function -as a filter. For this, we can use the ``simple_filter`` decorator: +For many cases, this will be unneccessarily complicated. +It's likely that we'd only want to register a single function as a filter. For this, we can use the ``simple_filter`` decorator: .. code-block:: json diff --git a/docs/advanced/new_line_characters.rst b/docs/advanced/new_line_characters.rst index 64f009898..e75ca9fcc 100644 --- a/docs/advanced/new_line_characters.rst +++ b/docs/advanced/new_line_characters.rst @@ -5,19 +5,22 @@ Working with line-ends special symbols LF/CRLF *New in Cookiecutter 2.0* -Before version 2.0 Cookiecutter silently used system line end character. -LF for POSIX and CRLF for Windows. Since version 2.0 this behaviour changed -and now can be forced at template level. +.. note:: -By default Cookiecutter now check every file at render stage and use same line -end as in source. This allow template developers to have both types of files in -the same template. Developers should correctly configure their `.gitattributes` -file to avoid line-end character overwrite by git. + Before version 2.0 Cookiecutter silently used system line end character. + LF for POSIX and CRLF for Windows. + Since version 2.0 this behaviour changed and now can be forced at template level. -Special template variable `_new_lines` was added in Cookiecutter 2.0. -Acceptable variables: `'\n\r'` for CRLF and `'\n'` for POSIX. +By default Cookiecutter checks every file at render stage and uses the same line end as in source. +This allow template developers to have both types of files in the same template. +Developers should correctly configure their ``.gitattributes`` file to avoid line-end character overwrite by git. -Here is example how to force line endings to CRLF on any deployment:: +The special template variable ``_new_lines`` enforces a specific line ending. +Acceptable variables: ``'\n\r'`` for CRLF and ``'\n'`` for POSIX. + +Here is example how to force line endings to CRLF on any deployment: + +.. code-block:: JSON { "project_slug": "sample", diff --git a/docs/advanced/private_variables.rst b/docs/advanced/private_variables.rst index 08ba9d943..6d17268b3 100644 --- a/docs/advanced/private_variables.rst +++ b/docs/advanced/private_variables.rst @@ -3,7 +3,12 @@ Private Variables ----------------- -Cookiecutter allows the definition private variables - those the user will not be required to fill in - by prepending an underscore to the variable name. These can either be not rendered, by using a prepending underscore, or rendered, prepending a double underscore. For example, the ``cookiecutter.json``:: +Cookiecutter allows the definition private variables by prepending an underscore to the variable name. +The user will not be required to fill those variables in. +These can either be not rendered, by using a prepending underscore, or rendered, prepending a double underscore. +For example, the ``cookiecutter.json``: + +.. code-block:: JSON { "project_name": "Really cool project", @@ -11,7 +16,9 @@ Cookiecutter allows the definition private variables - those the user will not b "__rendered": "{{ cookiecutter.project_name|lower }}" } -Will be rendered as:: +Will be rendered as: + +.. code-block:: JSON { "project_name": "Really cool project", @@ -21,7 +28,11 @@ Will be rendered as:: The user will only be asked for ``project_name``. -Non-rendered private variables can be used for defining constants. An example of where you may wish to use private **rendered** variables is creating a Python package repository and want to enforce naming consistency. To ensure the repository and package name are based on the project name, you could create a ``cookiecutter.json`` such as:: +Non-rendered private variables can be used for defining constants. +An example of where you may wish to use private **rendered** variables is creating a Python package repository and want to enforce naming consistency. +To ensure the repository and package name are based on the project name, you could create a ``cookiecutter.json`` such as: + +.. code-block:: JSON { "project_name": "Project Name", diff --git a/docs/advanced/replay.rst b/docs/advanced/replay.rst index 14afbfd24..426f0274e 100644 --- a/docs/advanced/replay.rst +++ b/docs/advanced/replay.rst @@ -9,7 +9,9 @@ On invocation **Cookiecutter** dumps a json file to ``~/.cookiecutter_replay/`` 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``):: +Example for a replay file (which was created via ``cookiecutter gh:hackebrot/cookiedozer``): + +.. code-block:: JSON { "cookiecutter": { @@ -28,27 +30,31 @@ Example for a replay file (which was created via ``cookiecutter gh:hackebrot/coo 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:: +Pass the according option on the CLI: + +.. code-block:: bash cookiecutter --replay gh:hackebrot/cookiedozer Or use the Python API:: +.. code-block:: python + 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. +This feature comes in handy if, for instance, you want to create a new project from an updated template. Custom replay file ~~~~~~~~~~~~~~~~~~ *New in Cookiecutter 2.0* -To specify a custom filename, you can use the ``--replay-file`` option:: +To specify a custom filename, you can use the ``--replay-file`` option: + +.. code-block:: bash cookiecutter --replay-file ./cookiedozer.json gh:hackebrot/cookiedozer -This may be useful to run the same replay file over several machines, in tests -or when a user of the template reports a problem +This may be useful to run the same replay file over several machines, in tests or when a user of the template reports a problem. diff --git a/docs/advanced/suppressing_prompts.rst b/docs/advanced/suppressing_prompts.rst index 1f73ff9fd..1064ad965 100644 --- a/docs/advanced/suppressing_prompts.rst +++ b/docs/advanced/suppressing_prompts.rst @@ -3,12 +3,14 @@ Suppressing Command-Line Prompts -------------------------------- -To suppress the prompts asking for input, use `no_input`. +To suppress the prompts asking for input, use ``no_input``. Basic Example: Using the Defaults ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Cookiecutter will pick a default value if used with `no_input`:: +Cookiecutter will pick a default value if used with ``no_input``: + +.. code-block:: python from cookiecutter.main import cookiecutter cookiecutter( @@ -16,18 +18,21 @@ Cookiecutter will pick a default value if used with `no_input`:: no_input=True, ) -In this case it will be using the default defined in `cookiecutter.json` or `.cookiecutterrc`. +In this case it will be using the default defined in ``cookiecutter.json`` or ``.cookiecutterrc``. .. note:: - values from `cookiecutter.json` will be overridden by values from `.cookiecutterrc` + values from ``cookiecutter.json`` will be overridden by values from ``.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:: +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: + +.. code-block:: JSON cookiecutter('cookiecutter-pypackage/', no_input=True, extra_context={'project_name': 'TheGreatest'}) -See the :ref:`API Reference ` for more details. + +See also :ref:`injecting-extra-content` and the :ref:`API Reference ` for more details. diff --git a/docs/advanced/template_extensions.rst b/docs/advanced/template_extensions.rst index 2e2273e8b..397460fa8 100644 --- a/docs/advanced/template_extensions.rst +++ b/docs/advanced/template_extensions.rst @@ -5,8 +5,8 @@ Template Extensions *New in Cookiecutter 1.4* -A template may extend the Cookiecutter environment with custom `Jinja2 extensions`_, -that can add extra filters, tests, globals or even extend the parser. +A template may extend the Cookiecutter environment with custom `Jinja2 extensions`_. +It can add extra filters, tests, globals or even extend the parser. To do so, a template author must specify the required extensions in ``cookiecutter.json`` as follows: @@ -20,12 +20,10 @@ To do so, a template author must specify the required extensions in ``cookiecutt On invocation Cookiecutter tries to import the extensions and add them to its environment respectively. -In the above example, Cookiecutter provides the additional tag `now`_, after -installing the `jinja2_time.TimeExtension`_ and enabling it in ``cookiecutter.json``. +In the above example, Cookiecutter provides the additional tag `now`_, after installing the `jinja2_time.TimeExtension`_ and enabling it in ``cookiecutter.json``. Please note that Cookiecutter will **not** install any dependencies on its own! -As a user you need to make sure you have all the extensions installed, before -running Cookiecutter on a template that requires custom Jinja2 extensions. +As a user you need to make sure you have all the extensions installed, before running Cookiecutter on a template that requires custom Jinja2 extensions. By default Cookiecutter includes the following extensions: @@ -38,8 +36,7 @@ By default Cookiecutter includes the following extensions: Jsonify extension ~~~~~~~~~~~~~~~~~ -The ``cookiecutter.extensions.JsonifyExtension`` extension provides a ``jsonify`` filter in templates -that converts a Python object to JSON: +The ``cookiecutter.extensions.JsonifyExtension`` extension provides a ``jsonify`` filter in templates that converts a Python object to JSON: .. code-block:: jinja @@ -56,10 +53,10 @@ Random string extension *New in Cookiecutter 1.7* -The ``cookiecutter.extensions.RandomStringExtension`` extension provides a ``random_ascii_string`` -method in templates that generates a random fixed-length string, optionally with punctuation. +The ``cookiecutter.extensions.RandomStringExtension`` extension provides a ``random_ascii_string`` method in templates that generates a random fixed-length string, optionally with punctuation. -Generate a random n-size character string. Example for n=12: +Generate a random n-size character string. +Example for n=12: .. code-block:: jinja @@ -71,8 +68,7 @@ Outputs: bIIUczoNvswh -The second argument controls if punctuation and special characters -``!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~`` should be present in the result: +The second argument controls if punctuation and special characters ``!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~`` should be present in the result: .. code-block:: jinja @@ -87,8 +83,7 @@ Outputs: Slugify extension ~~~~~~~~~~~~~~~~~ -The ``cookiecutter.extensions.SlugifyExtension`` extension provides a ``slugify`` filter in templates -that converts string into its underscored ("slugified") version: +The ``cookiecutter.extensions.SlugifyExtension`` extension provides a ``slugify`` filter in templates that converts string into its dashed ("slugified") version: .. code-block:: jinja @@ -100,16 +95,14 @@ Would output: it-s-a-random-version -It is diferent from a mere replace of spaces since it also trates some special characters -such as `'` in the example above. The function accepts all arguments that can be passed to -the `slugify` function of python-slugify. For example to change the output from -`it-s-a-random-version` to `it_s_a_random_version`, the parameter `separator='_'` would -be passed to `slugify()`. +It is different from a mere replace of spaces since it also trates some special characters such as ``'`` in the example above. +The function accepts all arguments that can be passed to the ``slugify`` function of `python-slugify`_. +For example to change the output from ``it-s-a-random-version``` to ``it_s_a_random_version``, the ``separator`` parameter would be passed: ``slugify(separator='_')``. .. _`Jinja2 extensions`: http://jinja.pocoo.org/docs/latest/extensions/ .. _`now`: https://github.com/hackebrot/jinja2-time#now-tag .. _`jinja2_time.TimeExtension`: https://github.com/hackebrot/jinja2-time -.. _`python-slugify`: https://github.com/un33k/python-slugify +.. _`python-slugify`: https://pypi.org/project/python-slugify UUID4 extension ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/advanced/templates_in_context.rst b/docs/advanced/templates_in_context.rst index faccb3c72..f324c60fe 100644 --- a/docs/advanced/templates_in_context.rst +++ b/docs/advanced/templates_in_context.rst @@ -4,20 +4,21 @@ 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. +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 +- a human-readable project name +- a lowercase, dashed repository name +- an importable, dash-less package name -Here is a `cookiecutter.json` with templated values for this pattern:: +Here is a `cookiecutter.json` with templated values for this pattern: + +.. code-block:: JSON { "project_name": "My New Project", @@ -25,13 +26,12 @@ Here is a `cookiecutter.json` with templated values for this pattern:: "pkg_name": "{{ cookiecutter.project_slug|replace('-', '') }}" } -If the user takes the defaults, or uses `no_input`, the templated values will -be: +If the user takes the defaults, or uses `no_input`, the templated values will be: -* `my-new-project` -* `mynewproject` +- `my-new-project` +- `mynewproject` Or, if the user gives `Yet Another New Project`, the values will be: -* `yet-another-new-project` -* `yetanothernewproject` +- ``yet-another-new-project`` +- ``yetanothernewproject`` diff --git a/docs/advanced/user_config.rst b/docs/advanced/user_config.rst index a46dc95b6..c7a8e9826 100644 --- a/docs/advanced/user_config.rst +++ b/docs/advanced/user_config.rst @@ -1,18 +1,26 @@ .. _user-config: -User Config (0.7.0+) -==================== +User Config +=========== + +*New in Cookiecutter 0.7* 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``:: +*New in Cookiecutter 1.3* + +You can also specify a config file on the command line via ``--config-file``. + +.. code-block:: bash - $ cookiecutter --config-file /home/audreyr/my-custom-config.yaml cookiecutter-pypackage + cookiecutter --config-file /home/audreyr/my-custom-config.yaml cookiecutter-pypackage -Or you can set the ``COOKIECUTTER_CONFIG`` environment variable:: +Or you can set the ``COOKIECUTTER_CONFIG`` environment variable: - $ export COOKIECUTTER_CONFIG=/home/audreyr/my-custom-config.yaml +.. code-block:: bash + + 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. @@ -36,7 +44,7 @@ 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. + 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`` @@ -46,5 +54,7 @@ Possible settings are: 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``. + 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), ``bb`` (Bitbucket), and ``gl`` (Gitlab) abbreviations shown above are actually **built in**, and can be used without defining them yourself. + +Read also: :ref:`injecting-extra-content` diff --git a/docs/tutorials/tutorial1.rst b/docs/tutorials/tutorial1.rst index 555753609..0871ca696 100644 --- a/docs/tutorials/tutorial1.rst +++ b/docs/tutorials/tutorial1.rst @@ -5,28 +5,25 @@ 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). +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. +*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/. +.. note:: + There are several variations of it. + 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. +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: +At the command line, run the cookiecutter command, passing in the link to cookiecutter-pypackage's HTTPS clone URL like this: .. code-block:: bash @@ -35,18 +32,17 @@ cookiecutter-pypackage's HTTPS clone URL like this: 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. +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. +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. +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 ---------------------------------- @@ -58,8 +54,7 @@ In your current directory, you should see that a project got generated: $ ls boilerplate -Looking inside the `boilerplate/` (or directory corresponding to your `project_slug`) -directory, you should see something like this: +Looking inside the `boilerplate/` (or directory corresponding to your `project_slug`) directory, you should see something like this: .. code-block:: bash @@ -94,38 +89,38 @@ 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? +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. +Let's take a look at cookiecutter-pypackage together. +Open https://github.com/audreyr/cookiecutter-pypackage in a new browser window. {{ cookiecutter.project_slug }} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Find the directory called `{{ cookiecutter.project_slug }}`. 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. +Find the directory called `{{ cookiecutter.project_slug }}`. +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. This happens in `find.py`, where the `find_template()` method looks for the first jinja-like directory name that starts with `cookiecutter`. AUTHORS.rst ~~~~~~~~~~~ -Look at the raw version of `{{ cookiecutter.project_slug }}/AUTHORS.rst`, at -https://raw.github.com/audreyr/cookiecutter-pypackage/master/%7B%7Bcookiecutter.project_slug%7D%7D/AUTHORS.rst. +Look at the raw version of `{{ cookiecutter.project_slug }}/AUTHORS.rst`, at https://raw.github.com/audreyr/cookiecutter-pypackage/master/%7B%7Bcookiecutter.project_slug%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. +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: +You should see JSON that corresponds to the prompts and default values shown earlier during project generation: .. code-block:: json @@ -147,13 +142,11 @@ earlier during project generation: 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. +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. +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. +In tutorial 2 (:ref:`tutorial2`), you'll see how to create cookiecutters of your own, from scratch. diff --git a/docs/tutorials/tutorial2.rst b/docs/tutorials/tutorial2.rst index 830206788..53fcc1d21 100644 --- a/docs/tutorials/tutorial2.rst +++ b/docs/tutorials/tutorial2.rst @@ -1,3 +1,5 @@ +.. _tutorial2: + ================================== Create a Cookiecutter From Scratch ================================== @@ -5,8 +7,7 @@ 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. +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: @@ -18,15 +19,13 @@ Create the directory for your cookiecutter and cd into it: Step 2: Create `project_slug` Directory --------------------------------------- -Create a directory called `{{ cookiecutter.project_slug }}`. +Create a directory called ``{{ cookiecutter.project_slug }}``. -This value will be replaced with the repo name of projects that you generate -from this cookiecutter. +This value will be replaced with the repo name of projects that you generate from this cookiecutter. Step 3: Create Files -------------------- -Inside of `{{ cookiecutter.project_slug }}`, create `index.html`, `site.css`, and -`site.js`. +Inside of ``{{ cookiecutter.project_slug }}``, create ``index.html``, ``site.css``, and ``site.js``. To be continued... From cd845ab8bc49d1e4544e8f1099caba03bde3b436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 30 May 2022 15:34:37 -0300 Subject: [PATCH 057/202] Fix typo in dict_variables.rst --- docs/advanced/dict_variables.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/advanced/dict_variables.rst b/docs/advanced/dict_variables.rst index 6142f4bd8..ccf1df499 100644 --- a/docs/advanced/dict_variables.rst +++ b/docs/advanced/dict_variables.rst @@ -39,7 +39,7 @@ For example, you could provide the following dictionary variable in your ``cooki } -The above ``file_type`` dictionary variable creates ``cookiecutter.file_types``, which can be used like this: +The above ``file_types`` dictionary variable creates ``cookiecutter.file_types``, which can be used like this: .. code-block:: html+jinja @@ -64,4 +64,3 @@ The above ``file_type`` dictionary variable creates ``cookiecutter.file_types``, Cookiecutter is using `Jinja2's for expression `_ to iterate over the items in the dictionary. - From 55047a61ba504417fc0ed32f246d883e1e7f76df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 30 May 2022 15:35:05 -0300 Subject: [PATCH 058/202] =?UTF-8?q?Add=20Jens=20and=20=C3=89rico=20to=20th?= =?UTF-8?q?e=20list=20of=20core=20contributors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AUTHORS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index d63785240..9e5a01494 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -14,6 +14,8 @@ - Sorin Sbarnea ([@ssbarnea](https://github.com/ssbarnea)) - Fábio C. Barrionuevo da Luz ([@luzfcb](https://github.com/luzfcb)) - Simone Basso ([@simobasso](https://github.com/simobasso)) +- Jens Klein ([@jensens](https://github.com/jensens)) +- Érico Andrei ([@ericof](https://github.com/ericof)) ## Contributors @@ -169,7 +171,6 @@ - Tom Forbes ([@orf](https://github.com/orf)) - Xie Yanbo ([@xyb](https://github.com/xyb)) - Maxim Ivanov ([@ivanovmg](https://github.com/ivanovmg)) -- Jens Klein ([@jensens](https://github.com/jensens)) ## Backers From e6b828a9a9ed53a4884ef30d957635be4b27c695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 30 May 2022 16:18:33 -0300 Subject: [PATCH 059/202] Follow PyPA guide to release package using GitHub Actions. --- .github/workflows/pip-publish.yml | 37 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pip-publish.yml b/.github/workflows/pip-publish.yml index a4803d54c..f9fae6871 100644 --- a/.github/workflows/pip-publish.yml +++ b/.github/workflows/pip-publish.yml @@ -8,19 +8,32 @@ jobs: upload: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v2 + - name: Set up Python uses: actions/setup-python@v1 with: - python-version: "3.7" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install setuptools wheel twine - - name: Package project - run: python setup.py sdist bdist_wheel - - name: Upload distributions - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - run: twine upload dist/* + python-version: 3.9 + + - name: Install pypa/build + run: >- + python -m + pip install + build + --user + + - name: Build a binary wheel and a source tarball + run: >- + python -m + build + --sdist + --wheel + --outdir dist/ + . + + - name: Publish a Python distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} From a7b1bc2ff92d1254614f49bf07aac62e57060a54 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 21:23:44 +0200 Subject: [PATCH 060/202] exists under docs, is enough --- case_studies.md | 25 ------------------------- docs/case_studies.md | 26 +++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 26 deletions(-) delete mode 100644 case_studies.md mode change 120000 => 100644 docs/case_studies.md diff --git a/case_studies.md b/case_studies.md deleted file mode 100644 index db101b979..000000000 --- a/case_studies.md +++ /dev/null @@ -1,25 +0,0 @@ -# Case Studies - -This showcase is where organizations can describe how they are using Cookiecutter. - -## [BeeWare](https://beeware.org/) - -Building Python tools for platforms like mobile phones and set top boxes requires a lot of boilerplate code just to get the project running. Cookiecutter has enabled us to very quickly stub out a starter project in which running Python code can be placed, and makes maintaining those templates very easy. With Cookiecutter we've been able to deliver support [Android devices](https://github.com/beeware/Python-Android-template), [iOS devices](https://github.com/beeware/Python-iOS-template), tvOS boxes, and we're planning to add native support for iOS and Windows devices in the future. - -[BeeWare](https://beeware.org/) is an organization building open source libraries for Python support on all platforms. - -## [ChrisDev](https://chrisdev.com/) - -Anytime we start a new project we begin with a [Cookiecutter template that generates a Django/Wagtail project](https://github.com/chrisdev/wagtail-cookiecutter-foundation) Our developers like it for maintainability and our designers enjoy being able to spin up new sites using our tool chain very quickly. Cookiecutter is very useful for because it supports both Mac OSX and Windows users. - -[ChrisDev](https://chrisdev.com/) is a Trinidad-based consulting agency. - -## [OpenStack](https://www.openstack.org/) - -OpenStack uses several Cookiecutter templates to generate: - -* [Openstack compliant puppet-modules](https://github.com/openstack/puppet-openstack-cookiecutter) -* [Install guides](https://github.com/openstack/installguide-cookiecutter) -* [New tempest plugins](https://github.com/openstack/tempest-plugin-cookiecutter) - -[OpenStack](https://www.openstack.org/) is open source software for creating private and public clouds. diff --git a/docs/case_studies.md b/docs/case_studies.md deleted file mode 120000 index 214ddc2b6..000000000 --- a/docs/case_studies.md +++ /dev/null @@ -1 +0,0 @@ -../case_studies.md \ No newline at end of file diff --git a/docs/case_studies.md b/docs/case_studies.md new file mode 100644 index 000000000..db101b979 --- /dev/null +++ b/docs/case_studies.md @@ -0,0 +1,25 @@ +# Case Studies + +This showcase is where organizations can describe how they are using Cookiecutter. + +## [BeeWare](https://beeware.org/) + +Building Python tools for platforms like mobile phones and set top boxes requires a lot of boilerplate code just to get the project running. Cookiecutter has enabled us to very quickly stub out a starter project in which running Python code can be placed, and makes maintaining those templates very easy. With Cookiecutter we've been able to deliver support [Android devices](https://github.com/beeware/Python-Android-template), [iOS devices](https://github.com/beeware/Python-iOS-template), tvOS boxes, and we're planning to add native support for iOS and Windows devices in the future. + +[BeeWare](https://beeware.org/) is an organization building open source libraries for Python support on all platforms. + +## [ChrisDev](https://chrisdev.com/) + +Anytime we start a new project we begin with a [Cookiecutter template that generates a Django/Wagtail project](https://github.com/chrisdev/wagtail-cookiecutter-foundation) Our developers like it for maintainability and our designers enjoy being able to spin up new sites using our tool chain very quickly. Cookiecutter is very useful for because it supports both Mac OSX and Windows users. + +[ChrisDev](https://chrisdev.com/) is a Trinidad-based consulting agency. + +## [OpenStack](https://www.openstack.org/) + +OpenStack uses several Cookiecutter templates to generate: + +* [Openstack compliant puppet-modules](https://github.com/openstack/puppet-openstack-cookiecutter) +* [Install guides](https://github.com/openstack/installguide-cookiecutter) +* [New tempest plugins](https://github.com/openstack/tempest-plugin-cookiecutter) + +[OpenStack](https://www.openstack.org/) is open source software for creating private and public clouds. From 2a819d754c596ebb20d48ed4b73028adf153b769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 30 May 2022 16:28:37 -0300 Subject: [PATCH 061/202] Explicitly use GitHub Actions environment --- .github/workflows/pip-publish.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/pip-publish.yml b/.github/workflows/pip-publish.yml index f9fae6871..c5e01d9e1 100644 --- a/.github/workflows/pip-publish.yml +++ b/.github/workflows/pip-publish.yml @@ -7,6 +7,11 @@ on: jobs: upload: runs-on: ubuntu-latest + + environment: + name: pypi.org + url: https://pypi.org/project/cookiecutter/ + steps: - uses: actions/checkout@v2 From 563472e88b7e83bcac45143ca0074ba2c7296211 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 21:34:40 +0200 Subject: [PATCH 062/202] add check-manifest to pre-commit and fix Manifest.in --- .pre-commit-config.yaml | 4 ++++ BACKERS.md | 9 --------- MANIFEST.in | 12 ++++++++++-- 3 files changed, 14 insertions(+), 11 deletions(-) delete mode 100644 BACKERS.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ba7fa3e1..7be7e3bb8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,3 +39,7 @@ repos: hooks: - id: bandit args: [--ini, .bandit] + - repo: https://github.com/mgedmin/check-manifest + rev: "0.48" + hooks: + - id: check-manifest \ No newline at end of file diff --git a/BACKERS.md b/BACKERS.md deleted file mode 100644 index 0c120e061..000000000 --- a/BACKERS.md +++ /dev/null @@ -1,9 +0,0 @@ -# Backers - -We would like to thank the following people for supporting us in our efforts to maintain and improve Cookiecutter: - -* Alex DeBrie -* Alexandre Y. Harano -* Bruno Alla -* Carol Willing -* Russell Keith-Magee diff --git a/MANIFEST.in b/MANIFEST.in index 2a2838a1d..0e1034643 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,11 +1,19 @@ include AUTHORS.md +include CODE_OF_CONDUCT.md include CONTRIBUTING.md include HISTORY.md include LICENSE include README.md +exclude Makefile +exclude __main__.py +exclude .* +exclude codecov.yml +exclude test_requirements.txt +exclude tox.ini + recursive-include tests * recursive-exclude * __pycache__ recursive-exclude * *.py[co] - -recursive-include docs *.rst conf.py Makefile make.bat +recursive-exclude docs * +recursive-exclude logo * From 96c68260eac572505f33381e627ad42b61aef357 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 30 May 2022 21:49:48 +0200 Subject: [PATCH 063/202] bump version and edit historie --- HISTORY.md | 28 ++++++++++++++++++++++++++++ cookiecutter/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b39699c1b..edef710f0 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,34 @@ History is important, but our current roadmap can be found [here](https://github.com/cookiecutter/cookiecutter/projects) +## 2.1.0 (2022-05-30) + +### Changes + +* Move contributors and backers to credits section (#1599) @doobrie +* test_generate_file_verbose_template_syntax_error fixed (#1671) @MaciejPatro +* Removed changes related to setuptools_scm (#1629) @ozer550 +* Feature/local extensions (#1240) @mwesterhof + +### CI/CD and QA changes + +* Check manifest: pre-commit, fixes, cleaning (#1683) @jensens +* Follow PyPA guide to release package using GitHub Actions. (#1682) @ericof + +### Documentation updates + +* Fix typo in dict_variables.rst (#1680) @ericof +* Documentation overhaul (#1677) @jensens +* Fixed incorrect link on docs. (#1649) @luzfcb + +### Bugfixes + +* Restore accidentally deleted support for click 8.x (#1643) @jaklan + +### This release was made possible by our wonderful contributors: + +@doobrie, @jensens, @ericof, @luzfcb + ## 2.0.2 (2021-12-27) *Remark: This release never made it to official PyPI* diff --git a/cookiecutter/__init__.py b/cookiecutter/__init__.py index eab68df8e..1075a8418 100644 --- a/cookiecutter/__init__.py +++ b/cookiecutter/__init__.py @@ -1,2 +1,2 @@ """Main package for Cookiecutter.""" -__version__ = "2.0.2" +__version__ = "2.1.0" diff --git a/setup.py b/setup.py index 9cb220978..5cd1729bc 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ """cookiecutter distutils configuration.""" from setuptools import setup -version = "2.0.3.dev0" +version = "2.1.0" with open('README.md', encoding='utf-8') as readme_file: readme = readme_file.read() From 58d716f51fda78ec793975eea5876691aa576b2c Mon Sep 17 00:00:00 2001 From: alkatar21 <61387986+alkatar21@users.noreply.github.com> Date: Tue, 31 May 2022 10:22:17 +0200 Subject: [PATCH 064/202] [Docs] Fix local extensions documentation --- docs/advanced/local_extensions.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/advanced/local_extensions.rst b/docs/advanced/local_extensions.rst index 9a3d4b940..d7eba4603 100644 --- a/docs/advanced/local_extensions.rst +++ b/docs/advanced/local_extensions.rst @@ -3,7 +3,7 @@ Local Extensions ---------------- -*New in Cookiecutter X.x* +*New in Cookiecutter 2.1* A template may extend the Cookiecutter environment with local extensions. These can be part of the template itself, providing it with more sophisticated custom tags and filters. @@ -18,13 +18,10 @@ To do so, a template author must specify the required extensions in ``cookiecutt "_extensions": ["local_extensions.FoobarExtension"] } -This example assumes that a ``local_extensions`` folder (python module) exists in the template root. -It will contain a ``main.py`` file, containing the following (for instance): +This example uses a simple module ``local_extensions.py`` which exists in the template root, containing the following (for instance): .. code-block:: python - # -*- coding: utf-8 -*- - from jinja2.ext import Extension @@ -48,8 +45,6 @@ It's likely that we'd only want to register a single function as a filter. For t .. code-block:: python - # -*- coding: utf-8 -*- - from cookiecutter.utils import simple_filter @@ -58,3 +53,7 @@ It's likely that we'd only want to register a single function as a filter. For t return v * 2 This snippet will achieve the exact same result as the previous one. + +For complex use cases, a python module ``local_extensions`` (a folder with an ``__init__.py``) can also be created in the template root. +Here, for example, a module ``main.py`` would have to export all extensions with ``from .main import FoobarExtension, simplefilterextension`` or ``from .main import *`` in the ``__init__.py``. + From 8b33e96c94ac75277e8f67cc1a71d90f488b5edb Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Tue, 31 May 2022 10:35:35 +0200 Subject: [PATCH 065/202] Bump version to 2.1.1.dev0 --- cookiecutter/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookiecutter/__init__.py b/cookiecutter/__init__.py index 1075a8418..88fb2f6e7 100644 --- a/cookiecutter/__init__.py +++ b/cookiecutter/__init__.py @@ -1,2 +1,2 @@ """Main package for Cookiecutter.""" -__version__ = "2.1.0" +__version__ = "2.1.1.dev0" diff --git a/setup.py b/setup.py index 5cd1729bc..907b0307f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ """cookiecutter distutils configuration.""" from setuptools import setup -version = "2.1.0" +version = "2.1.1.dev0" with open('README.md', encoding='utf-8') as readme_file: readme = readme_file.read() From e26c46582cd9033dcea318f1c29a1f06fb74f456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Tue, 31 May 2022 20:32:38 -0300 Subject: [PATCH 066/202] Sanitize Mercurial branch information before checkout. --- HISTORY.md | 2 ++ cookiecutter/vcs.py | 14 +++++++++----- tests/vcs/test_clone.py | 16 +++++++++++----- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index edef710f0..50f855ba8 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,8 @@ History is important, but our current roadmap can be found [here](https://github.com/cookiecutter/cookiecutter/projects) +## 2.1.1 (unreleased) + ## 2.1.0 (2022-05-30) ### Changes diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py index 08cb2eb0c..bb4356b31 100644 --- a/cookiecutter/vcs.py +++ b/cookiecutter/vcs.py @@ -98,8 +98,12 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): stderr=subprocess.STDOUT, ) if checkout is not None: + checkout_params = [checkout] + # Avoid Mercurial "--config" and "--debugger" injection vulnerability + if repo_type == "hg": + checkout_params.insert(0, "--") subprocess.check_output( # nosec - [repo_type, 'checkout', checkout], + [repo_type, 'checkout', *checkout_params], cwd=repo_dir, stderr=subprocess.STDOUT, ) @@ -107,13 +111,13 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): output = clone_error.output.decode('utf-8') if 'not found' in output.lower(): raise RepositoryNotFound( - 'The repository {} could not be found, ' - 'have you made a typo?'.format(repo_url) + f'The repository {repo_url} could not be found, ' + 'have you made a typo?' ) if any(error in output for error in BRANCH_ERRORS): raise RepositoryCloneFailed( - 'The {} branch of repository {} could not found, ' - 'have you made a typo?'.format(checkout, repo_url) + f'The {checkout} branch of repository ' + f'{repo_url} could not found, have you made a typo?' ) logger.error('git clone failed with error: %s', output) raise diff --git a/tests/vcs/test_clone.py b/tests/vcs/test_clone.py index 9fc3b24fa..ef62ef587 100644 --- a/tests/vcs/test_clone.py +++ b/tests/vcs/test_clone.py @@ -122,8 +122,14 @@ def test_clone_should_invoke_vcs_command( mock_subprocess.assert_any_call( [repo_type, 'clone', repo_url], cwd=str(clone_dir), stderr=subprocess.STDOUT ) + + branch_info = [branch] + # We sanitize branch information for Mercurial + if repo_type == "hg": + branch_info.insert(0, "--") + mock_subprocess.assert_any_call( - [repo_type, 'checkout', branch], cwd=expected_repo_dir, stderr=subprocess.STDOUT + [repo_type, 'checkout', *branch_info], cwd=expected_repo_dir, stderr=subprocess.STDOUT ) @@ -151,8 +157,8 @@ def test_clone_handles_repo_typo(mocker, clone_dir, error_message): vcs.clone(repository_url, clone_to_dir=str(clone_dir), no_input=True) assert str(err.value) == ( - 'The repository {} could not be found, have you made a typo?' - ).format(repository_url) + f'The repository {repository_url} could not be found, have you made a typo?' + ) @pytest.mark.parametrize( @@ -182,8 +188,8 @@ def test_clone_handles_branch_typo(mocker, clone_dir, error_message): assert str(err.value) == ( 'The unknown_branch branch of repository ' - '{} could not found, have you made a typo?' - ).format(repository_url) + f'{repository_url} could not found, have you made a typo?' + ) def test_clone_unknown_subprocess_error(mocker, clone_dir): From 85a7884f11a5200535706a6c5d31a9acbdadae1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Tue, 31 May 2022 20:37:22 -0300 Subject: [PATCH 067/202] Lint fixes --- tests/vcs/test_clone.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/vcs/test_clone.py b/tests/vcs/test_clone.py index ef62ef587..bd19ef1ab 100644 --- a/tests/vcs/test_clone.py +++ b/tests/vcs/test_clone.py @@ -129,7 +129,9 @@ def test_clone_should_invoke_vcs_command( branch_info.insert(0, "--") mock_subprocess.assert_any_call( - [repo_type, 'checkout', *branch_info], cwd=expected_repo_dir, stderr=subprocess.STDOUT + [repo_type, 'checkout', *branch_info], + cwd=expected_repo_dir, + stderr=subprocess.STDOUT, ) From 23182a564d19f5de753a58e70ab47f7c008ce0a5 Mon Sep 17 00:00:00 2001 From: Maciej Patro Date: Sat, 7 May 2022 01:35:35 +0200 Subject: [PATCH 068/202] Add CLI option to keep project files on failure. Fixes #1631 --- cookiecutter/cli.py | 7 +++++++ cookiecutter/generate.py | 5 ++++- cookiecutter/main.py | 4 ++++ tests/test_cli.py | 9 +++++++++ tests/test_generate_files.py | 12 ++++++++++++ tests/test_specify_output_dir.py | 2 ++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py index a792fa5f5..208901a92 100644 --- a/cookiecutter/cli.py +++ b/cookiecutter/cli.py @@ -144,6 +144,11 @@ def list_installed_templates(default_config, passed_config_file): @click.option( '-l', '--list-installed', is_flag=True, help='List currently installed templates.' ) +@click.option( + '--keep-project-on-failure', + is_flag=True, + help='Do not delete project folder on failure', +) def main( template, extra_context, @@ -161,6 +166,7 @@ def main( accept_hooks, replay_file, list_installed, + keep_project_on_failure, ): """Create a project from a Cookiecutter project template (TEMPLATE). @@ -205,6 +211,7 @@ def main( directory=directory, skip_if_file_exists=skip_if_file_exists, accept_hooks=_accept_hooks, + keep_project_on_failure=keep_project_on_failure, ) except ( ContextDecodingException, diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index 7bdce5a8b..cd7d34df2 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -268,6 +268,7 @@ def generate_files( overwrite_if_exists=False, skip_if_file_exists=False, accept_hooks=True, + keep_project_on_failure=False, ): """Render the templates and saves them to files. @@ -277,6 +278,8 @@ def generate_files( :param overwrite_if_exists: Overwrite the contents of the output directory if it exists. :param accept_hooks: Accept pre and post hooks if set to `True`. + :param keep_project_on_failure: If `True` keep generated project directory even when + generation fails """ template_dir = find_template(repo_dir) logger.debug('Generating project from %s...', template_dir) @@ -307,7 +310,7 @@ def generate_files( # if we created the output directory, then it's ok to remove it # if rendering fails - delete_project_on_failure = output_directory_created + delete_project_on_failure = output_directory_created and not keep_project_on_failure if accept_hooks: _run_hook_from_repo_dir( diff --git a/cookiecutter/main.py b/cookiecutter/main.py index bc2f262df..64a686ad1 100644 --- a/cookiecutter/main.py +++ b/cookiecutter/main.py @@ -34,6 +34,7 @@ def cookiecutter( directory=None, skip_if_file_exists=False, accept_hooks=True, + keep_project_on_failure=False, ): """ Run Cookiecutter just as if using it from the command line. @@ -53,6 +54,8 @@ def cookiecutter( :param password: The password to use when extracting the repository. :param directory: Relative path to a cookiecutter template in a repository. :param accept_hooks: Accept pre and post hooks if set to `True`. + :param keep_project_on_failure: If `True` keep generated project directory even when + generation fails """ if replay and ((no_input is not False) or (extra_context is not None)): err_msg = ( @@ -118,6 +121,7 @@ def cookiecutter( skip_if_file_exists=skip_if_file_exists, output_dir=output_dir, accept_hooks=accept_hooks, + keep_project_on_failure=keep_project_on_failure, ) # Cleanup (if required) diff --git a/tests/test_cli.py b/tests/test_cli.py index ad6abd1e0..1bc2fdd55 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -109,6 +109,7 @@ def test_cli_replay(mocker, cli_runner): password=None, directory=None, accept_hooks=True, + keep_project_on_failure=False, ) @@ -135,6 +136,7 @@ def test_cli_replay_file(mocker, cli_runner): password=None, directory=None, accept_hooks=True, + keep_project_on_failure=False, ) @@ -170,6 +172,7 @@ def test_cli_exit_on_noinput_and_replay(mocker, cli_runner): password=None, directory=None, accept_hooks=True, + keep_project_on_failure=False, ) @@ -205,6 +208,7 @@ def test_run_cookiecutter_on_overwrite_if_exists_and_replay( password=None, directory=None, accept_hooks=True, + keep_project_on_failure=False, ) @@ -261,6 +265,7 @@ def test_cli_output_dir(mocker, cli_runner, output_dir_flag, output_dir): password=None, directory=None, accept_hooks=True, + keep_project_on_failure=False, ) @@ -305,6 +310,7 @@ def test_user_config(mocker, cli_runner, user_config_path): password=None, directory=None, accept_hooks=True, + keep_project_on_failure=False, ) @@ -335,6 +341,7 @@ def test_default_user_config_overwrite(mocker, cli_runner, user_config_path): password=None, directory=None, accept_hooks=True, + keep_project_on_failure=False, ) @@ -360,6 +367,7 @@ def test_default_user_config(mocker, cli_runner): password=None, directory=None, accept_hooks=True, + keep_project_on_failure=False, ) @@ -629,6 +637,7 @@ def test_cli_accept_hooks( directory=None, skip_if_file_exists=False, accept_hooks=expected, + keep_project_on_failure=False, ) diff --git a/tests/test_generate_files.py b/tests/test_generate_files.py index 4d6ef1113..9cf4929aa 100644 --- a/tests/test_generate_files.py +++ b/tests/test_generate_files.py @@ -390,6 +390,18 @@ def test_raise_undefined_variable_dir_name(output_dir, undefined_context): assert not Path(output_dir).joinpath('testproject').exists() +def test_keep_project_dir_on_failure(output_dir, undefined_context): + """Verify correct error raised when directory name cannot be rendered.""" + with pytest.raises(exceptions.UndefinedVariableInTemplate): + generate.generate_files( + repo_dir='tests/undefined-variable/dir-name/', + output_dir=output_dir, + context=undefined_context, + keep_project_on_failure=True, + ) + assert Path(output_dir).joinpath('testproject').exists() + + def test_raise_undefined_variable_dir_name_existing_project( output_dir, undefined_context ): diff --git a/tests/test_specify_output_dir.py b/tests/test_specify_output_dir.py index 56c9eda6a..c907f2855 100644 --- a/tests/test_specify_output_dir.py +++ b/tests/test_specify_output_dir.py @@ -57,6 +57,7 @@ def test_api_invocation(mocker, template, output_dir, context): skip_if_file_exists=False, output_dir=output_dir, accept_hooks=True, + keep_project_on_failure=False, ) @@ -73,4 +74,5 @@ def test_default_output_dir(mocker, template, context): skip_if_file_exists=False, output_dir='.', accept_hooks=True, + keep_project_on_failure=False, ) From f9376a96097086476ce9eb0b93297a471ae520e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Wed, 1 Jun 2022 13:43:52 -0300 Subject: [PATCH 069/202] Prepare release 2.1.1 --- HISTORY.md | 15 ++++++++++++++- cookiecutter/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 50f855ba8..fe58416ce 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,7 +2,20 @@ History is important, but our current roadmap can be found [here](https://github.com/cookiecutter/cookiecutter/projects) -## 2.1.1 (unreleased) +## 2.1.1 (2022-06-01) + +### Documentation updates + +* Fix local extensions documentation (#1686) @alkatar21 + +### Bugfixes + +* Sanitize Mercurial branch information before checkout. (#1689) @ericof + +### This release is made by wonderfull contributors: + +@alkatar21, @ericof and @jensens + ## 2.1.0 (2022-05-30) diff --git a/cookiecutter/__init__.py b/cookiecutter/__init__.py index 88fb2f6e7..f0e3a2c38 100644 --- a/cookiecutter/__init__.py +++ b/cookiecutter/__init__.py @@ -1,2 +1,2 @@ """Main package for Cookiecutter.""" -__version__ = "2.1.1.dev0" +__version__ = "2.1.1" diff --git a/setup.py b/setup.py index 907b0307f..6b057835e 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ """cookiecutter distutils configuration.""" from setuptools import setup -version = "2.1.1.dev0" +version = "2.1.1" with open('README.md', encoding='utf-8') as readme_file: readme = readme_file.read() From 8e3cf3671b1964d3f7d7987cb8ee704a1c809fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Wed, 1 Jun 2022 13:46:10 -0300 Subject: [PATCH 070/202] Back to development --- HISTORY.md | 3 +++ cookiecutter/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index fe58416ce..5f647aa3a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,9 @@ History is important, but our current roadmap can be found [here](https://github.com/cookiecutter/cookiecutter/projects) + +## 2.1.2 (unreleased) + ## 2.1.1 (2022-06-01) ### Documentation updates diff --git a/cookiecutter/__init__.py b/cookiecutter/__init__.py index f0e3a2c38..a54486529 100644 --- a/cookiecutter/__init__.py +++ b/cookiecutter/__init__.py @@ -1,2 +1,2 @@ """Main package for Cookiecutter.""" -__version__ = "2.1.1" +__version__ = "2.1.2.dev0" diff --git a/setup.py b/setup.py index 6b057835e..7c6a677b2 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ """cookiecutter distutils configuration.""" from setuptools import setup -version = "2.1.1" +version = "2.1.2.dev0" with open('README.md', encoding='utf-8') as readme_file: readme = readme_file.read() From b888fd4c17afc9b6993d9edec146d25d54930c8f Mon Sep 17 00:00:00 2001 From: Ryan Russell Date: Wed, 1 Jun 2022 16:14:58 -0500 Subject: [PATCH 071/202] Improve Docs Readability Signed-off-by: Ryan Russell --- docs/advanced/local_extensions.rst | 2 +- docs/advanced/template_extensions.rst | 2 +- docs/index.rst | 2 +- docs/overview.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/advanced/local_extensions.rst b/docs/advanced/local_extensions.rst index d7eba4603..a9eb0a3fa 100644 --- a/docs/advanced/local_extensions.rst +++ b/docs/advanced/local_extensions.rst @@ -32,7 +32,7 @@ This example uses a simple module ``local_extensions.py`` which exists in the te This will register the ``foobar`` filter for the template. -For many cases, this will be unneccessarily complicated. +For many cases, this will be unnecessarily complicated. It's likely that we'd only want to register a single function as a filter. For this, we can use the ``simple_filter`` decorator: .. code-block:: json diff --git a/docs/advanced/template_extensions.rst b/docs/advanced/template_extensions.rst index 397460fa8..3e10bf838 100644 --- a/docs/advanced/template_extensions.rst +++ b/docs/advanced/template_extensions.rst @@ -95,7 +95,7 @@ Would output: it-s-a-random-version -It is different from a mere replace of spaces since it also trates some special characters such as ``'`` in the example above. +It is different from a mere replace of spaces since it also treats some special characters differently such as ``'`` in the example above. The function accepts all arguments that can be passed to the ``slugify`` function of `python-slugify`_. For example to change the output from ``it-s-a-random-version``` to ``it_s_a_random_version``, the ``separator`` parameter would be passed: ``slugify(separator='_')``. diff --git a/docs/index.rst b/docs/index.rst index 14f5edbcf..7e82fc2db 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,7 +6,7 @@ Cookiecutter: Better Project Templates ====================================== -Cookiecutter creates projects from **cookiecutters** (project templates), e.g. Python package projects from Python package temnplates. +Cookiecutter creates projects from **cookiecutters** (project templates), e.g. Python package projects from Python package templates. Basics ------ diff --git a/docs/overview.rst b/docs/overview.rst index 6a1895614..dd06999ef 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -5,7 +5,7 @@ Overview Cookiecutter takes a template provided as a directory structure with template-files. Templates can be in located in the filesystem, as a ZIP-file or on a VCS-Server (Git/Hg) like GitHub. -It reads a settings file and prompts the user interactivly wether to change the settings. +It reads a settings file and prompts the user interactively whether or not to change the settings. Then it takes both and generates an output directory structure from it. From cb7fb883511954eea7068d665d61bcee08d08e27 Mon Sep 17 00:00:00 2001 From: ri0t Date: Tue, 19 Oct 2021 18:44:44 +0200 Subject: [PATCH 072/202] fixes wrong (renamed) repository owner for cookiecutter-pypackage --- HISTORY.md | 10 +++++----- docs/advanced/calling_from_python.rst | 2 +- docs/advanced/user_config.rst | 2 +- docs/overview.rst | 2 +- docs/tutorials/tutorial1.rst | 16 ++++++++-------- docs/usage.rst | 10 +++++----- tests/repository/test_is_repo_url.py | 6 +++--- tests/vcs/test_identify_repo.py | 8 ++++---- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 5f647aa3a..5f8626cb7 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -226,7 +226,7 @@ Other Changes: * Tests update: use sys.executable when invoking python in python 3 only environment thanks to [@vincentbernat](https://github.com/vincentbernat) (#1221) * Prevent `click` API v7.0 from showing choices when already shown, thanks to [@rly](https://github.com/rly) and [@luzfcb](https://github.com/luzfcb) (#1168) * Test the codebase with python3.8 beta on tox and travis-ci (#1206), thanks to [@mihrab34](https://github.com/mihrab34) -* Add a [CODE\_OF\_CONDUCT.md](https://github.com/audreyr/cookiecutter/blob/master/CODE_OF_CONDUCT.md) file to the project, thanks to [@andreagrandi](https://github.com/andreagrandi) (#1009) +* Add a [CODE\_OF\_CONDUCT.md](https://github.com/audreyfeldroy/cookiecutter/blob/master/CODE_OF_CONDUCT.md) file to the project, thanks to [@andreagrandi](https://github.com/andreagrandi) (#1009) * Update docstrings in `cookiecutter/main.py`, `cookiecutter/__init__.py`, and `cookiecutter/log.py` to follow the PEP 257 style guide, thanks to [@meahow](https://github.com/meahow) (#998, #999, #1000) * Update docstrings in `cookiecutter/utils.py` to follow the PEP 257 style guide, thanks to [@dornheimer](https://github.com/dornheimer)(#1026) * Fix grammar in *Choice Variables* documentation, thanks to [@jubrilissa](https://github.com/jubrilissa) (#1011) @@ -548,7 +548,7 @@ Other Changes: * Enable py35 support on Travis by using Python 3.5 as base Python ([@maiksensi](https://github.com/maiksensi) / #540) * If a filename is empty, do not generate. Log instead ([@iljabauer](https://github.com/iljabauer) / #444) -* Fix tests as per last changes in [cookiecutter-pypackage](https://github.com/audreyr/cookiecutter-pypackage), thanks to [@eliasdorneles](https://github.com/eliasdorneles)(#555). +* Fix tests as per last changes in [cookiecutter-pypackage](https://github.com/audreyfeldroy/cookiecutter-pypackage), thanks to [@eliasdorneles](https://github.com/eliasdorneles)(#555). * Removed deprecated cookiecutter-pylibrary-minimal from the list, thanks to [@ionelmc](https://github.com/ionelmc) (#556) * Moved to using rualmel.yaml instead of PyYAML, except for Windows users on Python 2.7, thanks to [@pydanny](https://github.com/pydanny) (#557) @@ -823,8 +823,8 @@ Other changes: # 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 -``` + $ cookiecutter https://github.com/audreyfeldroy/cookiecutter-pypackage.git +``` * Can now use Cookiecutter from Python as a package: @@ -835,7 +835,7 @@ Other changes: cookiecutter('cookiecutter-pypackage/') # Create project from the cookiecutter-pypackage.git repo template - cookiecutter('https://github.com/audreyr/cookiecutter-pypackage.git') + cookiecutter('https://github.com/audreyfeldroy/cookiecutter-pypackage.git') ``` * Internal refactor to remove any code that changes the working diff --git a/docs/advanced/calling_from_python.rst b/docs/advanced/calling_from_python.rst index b497b6bf0..c51b09f0b 100644 --- a/docs/advanced/calling_from_python.rst +++ b/docs/advanced/calling_from_python.rst @@ -13,7 +13,7 @@ You can use Cookiecutter from Python: cookiecutter('cookiecutter-pypackage/') # Create project from the cookiecutter-pypackage.git repo template - cookiecutter('https://github.com/audreyr/cookiecutter-pypackage.git') + cookiecutter('https://github.com/audreyfeldroy/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`. diff --git a/docs/advanced/user_config.rst b/docs/advanced/user_config.rst index c7a8e9826..59063b291 100644 --- a/docs/advanced/user_config.rst +++ b/docs/advanced/user_config.rst @@ -36,7 +36,7 @@ Example user config: 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 + pp: https://github.com/audreyfeldroy/cookiecutter-pypackage.git gh: https://github.com/{0}.git bb: https://bitbucket.org/{0} diff --git a/docs/overview.rst b/docs/overview.rst index dd06999ef..6c75f77fe 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -32,7 +32,7 @@ You must have: Beyond that, you can have whatever files/directories you want. -See https://github.com/audreyr/cookiecutter-pypackage for a real-world example +See https://github.com/audreyfeldroy/cookiecutter-pypackage for a real-world example of this. Output diff --git a/docs/tutorials/tutorial1.rst b/docs/tutorials/tutorial1.rst index 0871ca696..e7133fe91 100644 --- a/docs/tutorials/tutorial1.rst +++ b/docs/tutorials/tutorial1.rst @@ -14,9 +14,8 @@ 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. - For this tutorial we'll use the original version at https://github.com/audreyr/cookiecutter-pypackage/. +.. note:: There are several variations of it, but for this tutorial we'll use + the original version at https://github.com/audreyfeldroy/cookiecutter-pypackage/. Step 1: Generate a Python Package Project ------------------------------------------ @@ -27,7 +26,7 @@ At the command line, run the cookiecutter command, passing in the link to cookie .. code-block:: bash - $ cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git + $ cookiecutter https://github.com/audreyfeldroy/cookiecutter-pypackage.git Local Cloning of Project Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -95,8 +94,7 @@ 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. +Let's take a look at cookiecutter-pypackage together. Open https://github.com/audreyfeldroy/cookiecutter-pypackage in a new browser window. {{ cookiecutter.project_slug }} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -111,7 +109,8 @@ This happens in `find.py`, where the `find_template()` method looks for the firs AUTHORS.rst ~~~~~~~~~~~ -Look at the raw version of `{{ cookiecutter.project_slug }}/AUTHORS.rst`, at https://raw.github.com/audreyr/cookiecutter-pypackage/master/%7B%7Bcookiecutter.project_slug%7D%7D/AUTHORS.rst. +Look at the raw version of `{{ cookiecutter.project_slug }}/AUTHORS.rst`, at +https://raw.github.com/audreyfeldroy/cookiecutter-pypackage/master/%7B%7Bcookiecutter.project_slug%7D%7D/AUTHORS.rst. Observe how it corresponds to the `AUTHORS.rst` file that you generated. @@ -142,7 +141,8 @@ You should see JSON that corresponds to the prompts and default values shown ear 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. +If anything needs better explanation, please take a moment to file an issue at https://github.com/audreyfeldroy/cookiecutter/issues with what could be improved +about this tutorial. Summary ------- diff --git a/docs/usage.rst b/docs/usage.rst index 9b7b95fe5..872cbe2b7 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -7,7 +7,7 @@ Grab a Cookiecutter template First, clone a Cookiecutter project template:: - $ git clone git@github.com:audreyr/cookiecutter-pypackage.git + $ git clone https://github.com/audreyfeldroy/cookiecutter-pypackage.git Make your changes ----------------- @@ -40,14 +40,14 @@ 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 gh:audreyfeldroy/cookiecutter-pypackage Cookiecutter knows abbreviations for Github (``gh``), Bitbucket (``bb``), and GitLab (``gl``) projects, but you can also give it the full URL to any repository:: - $ cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git - $ cookiecutter git+ssh://git@github.com/audreyr/cookiecutter-pypackage.git + $ cookiecutter https://github.com/audreyfeldroy/cookiecutter-pypackage.git + $ cookiecutter git+ssh://git@github.com/audreyfeldroy/cookiecutter-pypackage.git $ cookiecutter hg+ssh://hg@bitbucket.org/audreyr/cookiecutter-pypackage You will be prompted to enter a bunch of project config values. (These are @@ -58,7 +58,7 @@ 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 + $ cookiecutter https://github.com/audreyfeldroy/cookiecutter-pypackage.git --checkout develop Works with private repos ------------------------ diff --git a/tests/repository/test_is_repo_url.py b/tests/repository/test_is_repo_url.py index 64238e02e..5591be0fc 100644 --- a/tests/repository/test_is_repo_url.py +++ b/tests/repository/test_is_repo_url.py @@ -25,8 +25,8 @@ def test_is_zip_file(zipfile): @pytest.fixture( params=[ 'gitolite@server:team/repo', - 'git@github.com:audreyr/cookiecutter.git', - 'https://github.com/audreyr/cookiecutter.git', + 'git@github.com:audreyfeldroy/cookiecutter.git', + 'https://github.com/cookiecutter/cookiecutter.git', 'git+https://private.com/gitrepo', 'hg+https://private.com/mercurialrepo', 'https://bitbucket.org/pokoli/cookiecutter.hg', @@ -65,7 +65,7 @@ def test_is_repo_url_for_local_urls(local_repo_url): def test_expand_abbreviations(): """Validate `repository.expand_abbreviations` correctly translate url.""" - template = 'gh:audreyr/cookiecutter-pypackage' + template = 'gh:audreyfeldroy/cookiecutter-pypackage' # This is not a valid repo url just yet! # First `repository.expand_abbreviations` needs to translate it diff --git a/tests/vcs/test_identify_repo.py b/tests/vcs/test_identify_repo.py index fe264edcd..bfb3d56a2 100644 --- a/tests/vcs/test_identify_repo.py +++ b/tests/vcs/test_identify_repo.py @@ -24,14 +24,14 @@ ), ('https://bitbucket.org/foo/bar.hg', 'hg', 'https://bitbucket.org/foo/bar.hg'), ( - 'https://github.com/audreyr/cookiecutter-pypackage.git', + 'https://github.com/audreyfeldroy/cookiecutter-pypackage.git', 'git', - 'https://github.com/audreyr/cookiecutter-pypackage.git', + 'https://github.com/audreyfeldroy/cookiecutter-pypackage.git', ), ( - 'https://github.com/audreyr/cookiecutter-pypackage', + 'https://github.com/audreyfeldroy/cookiecutter-pypackage', 'git', - 'https://github.com/audreyr/cookiecutter-pypackage', + 'https://github.com/audreyfeldroy/cookiecutter-pypackage', ), ( 'git@gitorious.org:cookiecutter-gitorious/cookiecutter-gitorious.git', From d3d163803f322cc69c6a99534e7ff80c3c7f959e Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Sun, 5 Jun 2022 23:46:17 +0300 Subject: [PATCH 073/202] More renames --- AUTHORS.md | 2 +- HISTORY.md | 16 ++++++++-------- docs/tutorials/index.rst | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 9e5a01494..6b734589d 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -191,7 +191,7 @@ Contributions include user testing, debugging, improving documentation, reviewin - Adam Chainz ([@adamchainz](https://github.com/adamchainz)) - Andrew Ittner ([@tephyr](https://github.com/tephyr)) -- Audrey Roy Greenfeld ([@audreyr](https://github.com/audreyr)) +- Audrey Roy Greenfeld ([@audreyr](https://github.com/audreyfeldroy)) - Carol Willing ([@willingc](https://github.com/willingc)) - Christopher Clarke ([@chrisdev](https://github.com/chrisdev)) - Citlalli Murillo ([@citmusa](https://github.com/citmusa)) diff --git a/HISTORY.md b/HISTORY.md index 5f8626cb7..097509753 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -403,7 +403,7 @@ Other Changes: * Refactor cookiecutter template identification, thanks to [@michaeljoseph](https://github.com/michaeljoseph) (#777) * Add a `cli_runner` test fixture to simplify CLI tests, thanks to [@hackebrot](https://github.com/hackebrot) (#790) * Add a check to ensure cookiecutter repositories have JSON context, thanks to [@michaeljoseph](https://github.com/michaeljoseph)(#782) -* Rename the internal function that determines whether a file should be rendered, thanks to [@audreyr](https://github.com/audreyr) for raising the issue and [@hackebrot](https://github.com/hackebrot)for the PR (#741, #802) +* Rename the internal function that determines whether a file should be rendered, thanks to [@audreyr](https://github.com/audreyfeldroy) for raising the issue and [@hackebrot](https://github.com/hackebrot)for the PR (#741, #802) * Fix typo in docs, thanks to [@mwarkentin](https://github.com/mwarkentin) (#828) * Fix broken link to *Invoke* docs, thanks to [@B3QL](https://github.com/B3QL) (#820) * Add documentation to `render_variable` function in `prompt.py`, thanks to [@pydanny](https://github.com/pydanny) (#678) @@ -481,11 +481,11 @@ Other Changes: * Removed xfail in test\_cookiecutters, thanks to [@hackebrot](https://github.com/hackebrot) (#618) * Removed django-cms-plugin on account of 404 error, thanks to [@mativs](https://github.com/mativs) and [@pydanny](https://github.com/pydanny) (#593) * Fixed docs/usage.rst, thanks to [@macrotim](https://github.com/macrotim) (#604) -* Update .gitignore to latest Python.gitignore and ignore PyCharm files, thanks to [@audreyr](https://github.com/audreyr) +* Update .gitignore to latest Python.gitignore and ignore PyCharm files, thanks to [@audreyr](https://github.com/audreyfeldroy) * Use open context manager to read context\_file in generate() function, thanks to [@hackebrot](https://github.com/hackebrot) (#607, #608) * Added documentation for choice variables, thanks to [@maiksensi](https://github.com/maiksensi) (#611) -* Set up Scrutinizer to check code quality, thanks to [@audreyr](https://github.com/audreyr) +* Set up Scrutinizer to check code quality, thanks to [@audreyr](https://github.com/audreyfeldroy) * Drop distutils support in setup.py, thanks to [@hackebrot](https://github.com/hackebrot) (#606, #609) * Change cookiecutter-pypackage-minimal link, thanks to [@kragniz](https://github.com/kragniz) (#614) * Fix typo in one of the template\'s description, thanks to [@ryanfreckleton](https://github.com/ryanfreckleton) (#643) @@ -671,7 +671,7 @@ The goal of this release was to allow for injection of extra context via the Coo Features: -* cookiecutter() now takes an optional extra\_context parameter, thanks to [@michaeljoseph](https://github.com/michaeljoseph), [@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), [@pfmoore](https://github.com/pfmoore), [@pydanny](https://github.com/pydanny), [@audreyr](https://github.com/audreyr) (#260). +* cookiecutter() now takes an optional extra\_context parameter, thanks to [@michaeljoseph](https://github.com/michaeljoseph), [@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), [@pfmoore](https://github.com/pfmoore), [@pydanny](https://github.com/pydanny), [@audreyr](https://github.com/audreyfeldroy) (#260). * Context is now injected into hooks, thanks to [@michaeljoseph](https://github.com/michaeljoseph) and [@dinopetrone](https://github.com/dinopetrone). * Moved all Python 2/3 compatibility code into cookiecutter.compat, making the eventual move to six easier, thanks to [@michaeljoseph](https://github.com/michaeljoseph) (#60, #102). * Added cookiecutterrc defined aliases for cookiecutters, thanks to [@pfmoore](https://github.com/pfmoore) (#246) @@ -711,12 +711,12 @@ Bug Fixes: Other Changes: -* [@audreyr](https://github.com/audreyr) formally accepted position as **BDFL of cookiecutter**. +* [@audreyr](https://github.com/audreyfeldroy) formally accepted position as **BDFL of cookiecutter**. * Elevated [@pydanny](https://github.com/pydanny), [@michaeljoseph](https://github.com/michaeljoseph), and [@pfmoore](https://github.com/pfmoore) to core committer status. -* Added Core Committer guide, by [@audreyr](https://github.com/audreyr). -* Generated apidocs from make docs, by [@audreyr](https://github.com/audreyr). +* Added Core Committer guide, by [@audreyr](https://github.com/audreyfeldroy). +* Generated apidocs from make docs, by [@audreyr](https://github.com/audreyfeldroy). * Added contributing command to the makedocs function, by [@pydanny](https://github.com/pydanny). -* Refactored contributing documentation, included adding core committer instructions, by [@pydanny](https://github.com/pydanny) and [@audreyr](https://github.com/audreyr). +* Refactored contributing documentation, included adding core committer instructions, by [@pydanny](https://github.com/pydanny) and [@audreyr](https://github.com/audreyfeldroy). * Do not convert input prompt to bytes, thanks to [@uranusjr](https://github.com/uranusjr) (#192). * Added troubleshooting info about Python 3.3 tests and tox. * Added documentation about command line arguments, thanks to [@saxix](https://github.com/saxix). diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst index 81e1b0dff..88848a848 100644 --- a/docs/tutorials/index.rst +++ b/docs/tutorials/index.rst @@ -30,7 +30,7 @@ External Links .. _`Extending our Cookiecutter template`: https://raphael.codes/blog/extending-our-cookiecutter-template/ .. _`Wrapping up our Cookiecutter template`: https://raphael.codes/blog/wrapping-up-our-cookiecutter-template/ -.. _`@audreyr`: https://github.com/audreyr +.. _`@audreyr`: https://github.com/audreyfeldroy .. _`@pydanny`: https://github.com/pydanny .. _`@hackebrot`: https://github.com/hackebrot .. _`@BruceEckel`: https://github.com/BruceEckel From 7907a69f16c370bea23b6647cdf4b90136f1b8b9 Mon Sep 17 00:00:00 2001 From: Miro Jelaska <2855316+miro-jelaska@users.noreply.github.com> Date: Tue, 14 Sep 2021 02:08:22 +0200 Subject: [PATCH 074/202] update Docs for Create a Cookiecutter From Scratch --- docs/tutorials/tutorial2.rst | 92 ++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/docs/tutorials/tutorial2.rst b/docs/tutorials/tutorial2.rst index 53fcc1d21..e866b9b35 100644 --- a/docs/tutorials/tutorial2.rst +++ b/docs/tutorials/tutorial2.rst @@ -4,28 +4,104 @@ Create a Cookiecutter From Scratch ================================== +In this tutorial, we are creating `cookiecutter-website-simple`, a cookiecutter for generating simple, bare-bones websites. + 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 cookiecutter.json +---------------------------------- + +`cookiecutter.json` is a JSON file that contains fields which can be referenced in the cookiecutter template. For each, default value is defined and user will be prompted for input during cookiecutter execution. Only mandatory field is `project_slug` and it should comply with package naming conventions defined in `PEP8 Naming Conventions `_ . + +.. code-block:: json + + { + "project_name": "Cookiecutter Website Simple", + "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_') }}", + "author": "Anonymous" + } -Step 2: Create `project_slug` Directory + +Step 3: Create project_slug Directory --------------------------------------- -Create a directory called ``{{ cookiecutter.project_slug }}``. +Create a directory called `{{ cookiecutter.project_slug }}`. This value will be replaced with the repo name of projects that you generate from this cookiecutter. -Step 3: Create Files --------------------- +Step 4: Create index.html +-------------------------- + +Inside of `{{ cookiecutter.project_slug }}`, create `index.html` with following content: + +.. code-block:: html + + + + + + {{ cookiecutter.project_name }} + + + +

{{ cookiecutter.project_name }}

+

by {{ cookiecutter.author }}

+ + + +Step 5: Pack cookiecutter into ZIP +---------------------------------- +There are many ways to run Cookiecutter templates, and they are described in details in `Usage chapter `_. In this tutorial we are going to ZIP cookiecutter and then run it for testing. + +By running following command `cookiecutter.zip` will get generated which can be used to run cookiecutter. Script will generate `cookiecutter.zip` ZIP file and echo full path to the file. + +.. code-block:: bash + + $ (SOURCE_DIR=$(basename $PWD) ZIP=cookiecutter.zip && # Set variables + pushd && # Set parent directory as working directory + zip -r $ZIP $SOURCE_DIR --exclude $SOURCE_DIR/$ZIP --quiet && # ZIP cookiecutter + mv $ZIP $SOURCE_DIR/$ZIP && # Move ZIP to original directory + popd && # Restore original work directory + echo "Cookiecutter full path: $PWD/$ZIP") + +Step 6: Run cookiecutter +------------------------ +Set your work directory to whatever directory you would like to run cookiecutter at. Use cookiecutter full path and run the following command: + +.. code-block:: bash + + $ cookiecutter + +You can expect similar output: + +.. code-block:: bash + + $ cookiecutter /Users/admin/cookiecutter-website-simple/cookiecutter.zip + project_name [Cookiecutter Website Simple]: Test web + project_slug [test_web]: + author [Anonymous]: Cookiecutter Developer + +Resulting directory should be inside your work directory with a name that matches `project_slug` you defined. Inside that direcory there should be `index.html` with generated source: + +.. code-block:: html -Inside of ``{{ cookiecutter.project_slug }}``, create ``index.html``, ``site.css``, and ``site.js``. + + + + + Test web + -To be continued... + +

Cookiecutter Developer

+

by Test web

+ + From a5197d05cf890c1e18931c83186c6d91c6c70e2f Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 04:27:25 +0300 Subject: [PATCH 075/202] Fix spaces for linting --- docs/tutorials/tutorial2.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/tutorials/tutorial2.rst b/docs/tutorials/tutorial2.rst index e866b9b35..202412537 100644 --- a/docs/tutorials/tutorial2.rst +++ b/docs/tutorials/tutorial2.rst @@ -15,7 +15,7 @@ Create the directory for your cookiecutter and cd into it: $ mkdir cookiecutter-website-simple $ cd cookiecutter-website-simple/ - + Step 2: Create cookiecutter.json ---------------------------------- @@ -56,39 +56,39 @@ Inside of `{{ cookiecutter.project_slug }}`, create `index.html` with following

by {{ cookiecutter.author }}

- + Step 5: Pack cookiecutter into ZIP ---------------------------------- -There are many ways to run Cookiecutter templates, and they are described in details in `Usage chapter `_. In this tutorial we are going to ZIP cookiecutter and then run it for testing. +There are many ways to run Cookiecutter templates, and they are described in details in `Usage chapter `_. In this tutorial we are going to ZIP cookiecutter and then run it for testing. By running following command `cookiecutter.zip` will get generated which can be used to run cookiecutter. Script will generate `cookiecutter.zip` ZIP file and echo full path to the file. .. code-block:: bash - + $ (SOURCE_DIR=$(basename $PWD) ZIP=cookiecutter.zip && # Set variables pushd && # Set parent directory as working directory zip -r $ZIP $SOURCE_DIR --exclude $SOURCE_DIR/$ZIP --quiet && # ZIP cookiecutter mv $ZIP $SOURCE_DIR/$ZIP && # Move ZIP to original directory popd && # Restore original work directory echo "Cookiecutter full path: $PWD/$ZIP") - + Step 6: Run cookiecutter ------------------------ Set your work directory to whatever directory you would like to run cookiecutter at. Use cookiecutter full path and run the following command: .. code-block:: bash - + $ cookiecutter - + You can expect similar output: .. code-block:: bash - + $ cookiecutter /Users/admin/cookiecutter-website-simple/cookiecutter.zip project_name [Cookiecutter Website Simple]: Test web project_slug [test_web]: author [Anonymous]: Cookiecutter Developer - + Resulting directory should be inside your work directory with a name that matches `project_slug` you defined. Inside that direcory there should be `index.html` with generated source: .. code-block:: html From fcd417de9c1917c59535e7adcc7ae36cb9717c3d Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 05:29:37 +0300 Subject: [PATCH 076/202] Replace jinja.pocoo.org to https://jinja.palletsprojects.com/en --- docs/advanced/dict_variables.rst | 2 +- docs/advanced/template_extensions.rst | 2 +- docs/troubleshooting.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/advanced/dict_variables.rst b/docs/advanced/dict_variables.rst index ccf1df499..63241c0a0 100644 --- a/docs/advanced/dict_variables.rst +++ b/docs/advanced/dict_variables.rst @@ -63,4 +63,4 @@ The above ``file_types`` dictionary variable creates ``cookiecutter.file_types`` {% endfor %} -Cookiecutter is using `Jinja2's for expression `_ to iterate over the items in the dictionary. +Cookiecutter is using `Jinja2's for expression `_ to iterate over the items in the dictionary. diff --git a/docs/advanced/template_extensions.rst b/docs/advanced/template_extensions.rst index 3e10bf838..272c42b0c 100644 --- a/docs/advanced/template_extensions.rst +++ b/docs/advanced/template_extensions.rst @@ -99,7 +99,7 @@ It is different from a mere replace of spaces since it also treats some special The function accepts all arguments that can be passed to the ``slugify`` function of `python-slugify`_. For example to change the output from ``it-s-a-random-version``` to ``it_s_a_random_version``, the ``separator`` parameter would be passed: ``slugify(separator='_')``. -.. _`Jinja2 extensions`: http://jinja.pocoo.org/docs/latest/extensions/ +.. _`Jinja2 extensions`: https://jinja.palletsprojects.com/en/latest/extensions/ .. _`now`: https://github.com/hackebrot/jinja2-time#now-tag .. _`jinja2_time.TimeExtension`: https://github.com/hackebrot/jinja2-time .. _`python-slugify`: https://pypi.org/project/python-slugify diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index 2e45a91d5..cca4b82fd 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -25,7 +25,7 @@ Or this:: {{ {{ url_for('home') }} }} -See http://jinja.pocoo.org/docs/templates/#escaping for more info. +See https://jinja.palletsprojects.com/en/latest/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. From 3b5b5bfba33aa93430b6cac219be62031f67f770 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 05:41:04 +0300 Subject: [PATCH 077/202] Fix @audreyr to @audreyfeldroy --- AUTHORS.md | 2 +- HISTORY.md | 16 ++++++++-------- docs/tutorials/index.rst | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 6b734589d..76ebc2622 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -191,7 +191,7 @@ Contributions include user testing, debugging, improving documentation, reviewin - Adam Chainz ([@adamchainz](https://github.com/adamchainz)) - Andrew Ittner ([@tephyr](https://github.com/tephyr)) -- Audrey Roy Greenfeld ([@audreyr](https://github.com/audreyfeldroy)) +- Audrey Roy Greenfeld ([@audreyfeldroy](https://github.com/audreyfeldroy)) - Carol Willing ([@willingc](https://github.com/willingc)) - Christopher Clarke ([@chrisdev](https://github.com/chrisdev)) - Citlalli Murillo ([@citmusa](https://github.com/citmusa)) diff --git a/HISTORY.md b/HISTORY.md index 097509753..cc11b368a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -403,7 +403,7 @@ Other Changes: * Refactor cookiecutter template identification, thanks to [@michaeljoseph](https://github.com/michaeljoseph) (#777) * Add a `cli_runner` test fixture to simplify CLI tests, thanks to [@hackebrot](https://github.com/hackebrot) (#790) * Add a check to ensure cookiecutter repositories have JSON context, thanks to [@michaeljoseph](https://github.com/michaeljoseph)(#782) -* Rename the internal function that determines whether a file should be rendered, thanks to [@audreyr](https://github.com/audreyfeldroy) for raising the issue and [@hackebrot](https://github.com/hackebrot)for the PR (#741, #802) +* Rename the internal function that determines whether a file should be rendered, thanks to [@audreyfeldroy](https://github.com/audreyfeldroy) for raising the issue and [@hackebrot](https://github.com/hackebrot)for the PR (#741, #802) * Fix typo in docs, thanks to [@mwarkentin](https://github.com/mwarkentin) (#828) * Fix broken link to *Invoke* docs, thanks to [@B3QL](https://github.com/B3QL) (#820) * Add documentation to `render_variable` function in `prompt.py`, thanks to [@pydanny](https://github.com/pydanny) (#678) @@ -481,11 +481,11 @@ Other Changes: * Removed xfail in test\_cookiecutters, thanks to [@hackebrot](https://github.com/hackebrot) (#618) * Removed django-cms-plugin on account of 404 error, thanks to [@mativs](https://github.com/mativs) and [@pydanny](https://github.com/pydanny) (#593) * Fixed docs/usage.rst, thanks to [@macrotim](https://github.com/macrotim) (#604) -* Update .gitignore to latest Python.gitignore and ignore PyCharm files, thanks to [@audreyr](https://github.com/audreyfeldroy) +* Update .gitignore to latest Python.gitignore and ignore PyCharm files, thanks to [@audreyfeldroy](https://github.com/audreyfeldroy) * Use open context manager to read context\_file in generate() function, thanks to [@hackebrot](https://github.com/hackebrot) (#607, #608) * Added documentation for choice variables, thanks to [@maiksensi](https://github.com/maiksensi) (#611) -* Set up Scrutinizer to check code quality, thanks to [@audreyr](https://github.com/audreyfeldroy) +* Set up Scrutinizer to check code quality, thanks to [@audreyfeldroy](https://github.com/audreyfeldroy) * Drop distutils support in setup.py, thanks to [@hackebrot](https://github.com/hackebrot) (#606, #609) * Change cookiecutter-pypackage-minimal link, thanks to [@kragniz](https://github.com/kragniz) (#614) * Fix typo in one of the template\'s description, thanks to [@ryanfreckleton](https://github.com/ryanfreckleton) (#643) @@ -671,7 +671,7 @@ The goal of this release was to allow for injection of extra context via the Coo Features: -* cookiecutter() now takes an optional extra\_context parameter, thanks to [@michaeljoseph](https://github.com/michaeljoseph), [@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), [@pfmoore](https://github.com/pfmoore), [@pydanny](https://github.com/pydanny), [@audreyr](https://github.com/audreyfeldroy) (#260). +* cookiecutter() now takes an optional extra\_context parameter, thanks to [@michaeljoseph](https://github.com/michaeljoseph), [@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), [@pfmoore](https://github.com/pfmoore), [@pydanny](https://github.com/pydanny), [@audreyfeldroy](https://github.com/audreyfeldroy) (#260). * Context is now injected into hooks, thanks to [@michaeljoseph](https://github.com/michaeljoseph) and [@dinopetrone](https://github.com/dinopetrone). * Moved all Python 2/3 compatibility code into cookiecutter.compat, making the eventual move to six easier, thanks to [@michaeljoseph](https://github.com/michaeljoseph) (#60, #102). * Added cookiecutterrc defined aliases for cookiecutters, thanks to [@pfmoore](https://github.com/pfmoore) (#246) @@ -711,12 +711,12 @@ Bug Fixes: Other Changes: -* [@audreyr](https://github.com/audreyfeldroy) formally accepted position as **BDFL of cookiecutter**. +* [@audreyfeldroy](https://github.com/audreyfeldroy) formally accepted position as **BDFL of cookiecutter**. * Elevated [@pydanny](https://github.com/pydanny), [@michaeljoseph](https://github.com/michaeljoseph), and [@pfmoore](https://github.com/pfmoore) to core committer status. -* Added Core Committer guide, by [@audreyr](https://github.com/audreyfeldroy). -* Generated apidocs from make docs, by [@audreyr](https://github.com/audreyfeldroy). +* Added Core Committer guide, by [@audreyfeldroy](https://github.com/audreyfeldroy). +* Generated apidocs from make docs, by [@audreyfeldroy](https://github.com/audreyfeldroy). * Added contributing command to the makedocs function, by [@pydanny](https://github.com/pydanny). -* Refactored contributing documentation, included adding core committer instructions, by [@pydanny](https://github.com/pydanny) and [@audreyr](https://github.com/audreyfeldroy). +* Refactored contributing documentation, included adding core committer instructions, by [@pydanny](https://github.com/pydanny) and [@audreyfeldroy](https://github.com/audreyfeldroy). * Do not convert input prompt to bytes, thanks to [@uranusjr](https://github.com/uranusjr) (#192). * Added troubleshooting info about Python 3.3 tests and tox. * Added documentation about command line arguments, thanks to [@saxix](https://github.com/saxix). diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst index 88848a848..0fa1c98b0 100644 --- a/docs/tutorials/index.rst +++ b/docs/tutorials/index.rst @@ -2,7 +2,7 @@ Tutorials ==================== -Tutorials by `@audreyr`_ +Tutorials by `@audreyfeldroy`_ .. toctree:: :maxdepth: 2 @@ -30,7 +30,7 @@ External Links .. _`Extending our Cookiecutter template`: https://raphael.codes/blog/extending-our-cookiecutter-template/ .. _`Wrapping up our Cookiecutter template`: https://raphael.codes/blog/wrapping-up-our-cookiecutter-template/ -.. _`@audreyr`: https://github.com/audreyfeldroy +.. _`@audreyfeldroy`: https://github.com/audreyfeldroy .. _`@pydanny`: https://github.com/pydanny .. _`@hackebrot`: https://github.com/hackebrot .. _`@BruceEckel`: https://github.com/BruceEckel From ed4748f99d6f9d1f6a640f759fea72e1e3f2e034 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 05:41:53 +0300 Subject: [PATCH 078/202] Fix LICENSE years --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 06486a8f3..9661fad7a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2021, Audrey Roy Greenfeld +Copyright (c) 2013-2022, Audrey Roy Greenfeld All rights reserved. Redistribution and use in source and binary forms, with or From f4939ff5403316184a37c7d36a34ed261b198ab8 Mon Sep 17 00:00:00 2001 From: cksac Date: Mon, 6 Jun 2022 15:36:36 +0800 Subject: [PATCH 079/202] support partially overwrite keys in nested dict --- cookiecutter/generate.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index cd7d34df2..fcaf9f319 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -68,6 +68,10 @@ def apply_overwrites_to_context(context, overwrite_context): "{} provided for choice variable {}, but the " "choices are {}.".format(overwrite, variable, context_value) ) + if isinstance(context_value, dict) and isinstance(overwrite, dict): + # Partially overwrite some keys in original dict + apply_overwrites_to_context(context_value, overwrite) + context[variable] = context_value else: # Simply overwrite the value for this variable context[variable] = overwrite From ef839722d4449021ab1642b07b813a774a702692 Mon Sep 17 00:00:00 2001 From: Zhong Dai <4401438+zhongdai@users.noreply.github.com> Date: Wed, 2 Mar 2022 15:52:24 +1100 Subject: [PATCH 080/202] fixed the override not working with copy only dir --- cookiecutter/generate.py | 5 + .../{{cookiecutter.repo_name}}/README.rst | 5 + .../{{cookiecutter.repo_name}}/README.txt | 5 + .../rendered/not_rendered.yml | 2 + .../README.rst | 5 + .../README.md | 3 + .../README.rst | 5 + .../README.txt | 5 + ...t_generate_copy_without_render_override.py | 95 +++++++++++++++++++ 9 files changed, 130 insertions(+) create mode 100644 tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/README.rst create mode 100644 tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/README.txt create mode 100644 tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/rendered/not_rendered.yml create mode 100644 tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-not-rendered/README.rst create mode 100644 tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.md create mode 100644 tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.rst create mode 100644 tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.txt create mode 100644 tests/test_generate_copy_without_render_override.py diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index cd7d34df2..65d1f4076 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -333,6 +333,7 @@ def generate_files( # specified in the ``_copy_without_render`` setting, but # we store just the dir name if is_copy_only_path(d_, context): + logger.debug('Found copy only path %s', d) copy_dirs.append(d) else: render_dirs.append(d) @@ -342,6 +343,10 @@ def generate_files( outdir = os.path.normpath(os.path.join(project_dir, indir)) outdir = env.from_string(outdir).render(**context) logger.debug('Copying dir %s to %s without rendering', indir, outdir) + + # if the outdir is there, it must be a overwrite execution + if os.path.isdir(outdir): + shutil.rmtree(outdir) shutil.copytree(indir, outdir) # We mutate ``dirs``, because we only want to go through these dirs diff --git a/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/README.rst b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/README.rst new file mode 100644 index 000000000..2413548bf --- /dev/null +++ b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/README.rst @@ -0,0 +1,5 @@ +============ +Fake Project +============ + +{{cookiecutter.render_test}} diff --git a/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/README.txt b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/README.txt new file mode 100644 index 000000000..2413548bf --- /dev/null +++ b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/README.txt @@ -0,0 +1,5 @@ +============ +Fake Project +============ + +{{cookiecutter.render_test}} diff --git a/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/rendered/not_rendered.yml b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/rendered/not_rendered.yml new file mode 100644 index 000000000..a31cf752c --- /dev/null +++ b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/rendered/not_rendered.yml @@ -0,0 +1,2 @@ +--- +- name: {{cookiecutter.render_test}} diff --git a/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-not-rendered/README.rst b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-not-rendered/README.rst new file mode 100644 index 000000000..2413548bf --- /dev/null +++ b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-not-rendered/README.rst @@ -0,0 +1,5 @@ +============ +Fake Project +============ + +{{cookiecutter.render_test}} diff --git a/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.md b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.md new file mode 100644 index 000000000..0e74081d8 --- /dev/null +++ b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.md @@ -0,0 +1,3 @@ +# Fake Project + +{{cookiecutter.render_test}} diff --git a/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.rst b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.rst new file mode 100644 index 000000000..2413548bf --- /dev/null +++ b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.rst @@ -0,0 +1,5 @@ +============ +Fake Project +============ + +{{cookiecutter.render_test}} diff --git a/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.txt b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.txt new file mode 100644 index 000000000..2413548bf --- /dev/null +++ b/tests/test-generate-copy-without-render-override/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}-rendered/README.txt @@ -0,0 +1,5 @@ +============ +Fake Project +============ + +{{cookiecutter.render_test}} diff --git a/tests/test_generate_copy_without_render_override.py b/tests/test_generate_copy_without_render_override.py new file mode 100644 index 000000000..d0a798196 --- /dev/null +++ b/tests/test_generate_copy_without_render_override.py @@ -0,0 +1,95 @@ +"""Verify correct work of `_copy_without_render` context option.""" +import os + +import pytest + +from cookiecutter import generate +from cookiecutter import utils + + +@pytest.fixture +def remove_test_dir(): + """Fixture. Remove the folder that is created by the test.""" + yield + if os.path.exists('test_copy_without_render'): + utils.rmtree('test_copy_without_render') + + +@pytest.mark.usefixtures('clean_system', 'remove_test_dir') +def test_generate_copy_without_render_extensions(): + """Verify correct work of `_copy_without_render` context option. + + Some files/directories should be rendered during invocation, + some just copied, without any modification. + """ + + # first run + 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', + '{{cookiecutter.repo_name}}-rendered/README.md', + ], + } + }, + repo_dir='tests/test-generate-copy-without-render-override', + ) + + # second run with override flag to True + 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', + '{{cookiecutter.repo_name}}-rendered/README.md', + ], + } + }, + overwrite_if_exists=True, + repo_dir='tests/test-generate-copy-without-render', + ) + + dir_contents = os.listdir('test_copy_without_render') + + assert 'test_copy_without_render-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/' + 'test_copy_without_render-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() + + with open( + 'test_copy_without_render/' 'test_copy_without_render-rendered/' 'README.md' + ) as f: + assert '{{cookiecutter.render_test}}' in f.read() From cf3c85b266f89437498d7cf4e339d874bff0648e Mon Sep 17 00:00:00 2001 From: Zhong Dai <4401438+zhongdai@users.noreply.github.com> Date: Mon, 14 Mar 2022 19:58:37 +1100 Subject: [PATCH 081/202] remove the blank line after the docstring --- tests/test_generate_copy_without_render_override.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_generate_copy_without_render_override.py b/tests/test_generate_copy_without_render_override.py index d0a798196..af85c4b04 100644 --- a/tests/test_generate_copy_without_render_override.py +++ b/tests/test_generate_copy_without_render_override.py @@ -22,7 +22,6 @@ def test_generate_copy_without_render_extensions(): Some files/directories should be rendered during invocation, some just copied, without any modification. """ - # first run generate.generate_files( context={ From 855a1c3fc2c603a38298bfe5c5c5723f7a0031b2 Mon Sep 17 00:00:00 2001 From: Zhong Dai <4401438+zhongdai@users.noreply.github.com> Date: Mon, 11 Apr 2022 15:35:51 +1000 Subject: [PATCH 082/202] add extra check for the overwrite_if_exists flag Co-authored-by: Andrew Tribone <780132+att14@users.noreply.github.com> --- cookiecutter/generate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index 65d1f4076..8350df0f9 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -345,7 +345,7 @@ def generate_files( logger.debug('Copying dir %s to %s without rendering', indir, outdir) # if the outdir is there, it must be a overwrite execution - if os.path.isdir(outdir): + if os.path.isdir(outdir) and overwrite_if_exists: shutil.rmtree(outdir) shutil.copytree(indir, outdir) From c7729fa88b9c7b929d4de76c0d88a3675119d210 Mon Sep 17 00:00:00 2001 From: Zhong Dai <4401438+zhongdai@users.noreply.github.com> Date: Wed, 13 Apr 2022 09:06:58 +1000 Subject: [PATCH 083/202] added the function doc for skip_if_file_exists --- cookiecutter/generate.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index 8350df0f9..ce2bc0ab9 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -277,6 +277,8 @@ def generate_files( :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. + :param skip_if_file_exists: Skip the files in the corresponding directories + if they already exist :param accept_hooks: Accept pre and post hooks if set to `True`. :param keep_project_on_failure: If `True` keep generated project directory even when generation fails From 418e316d36e440697e3d14c76ef5490175c3a094 Mon Sep 17 00:00:00 2001 From: Zhong Dai <4401438+zhongdai@users.noreply.github.com> Date: Wed, 13 Apr 2022 09:25:53 +1000 Subject: [PATCH 084/202] fixed the exception test case --- cookiecutter/generate.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index ce2bc0ab9..605b8952a 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -346,8 +346,10 @@ def generate_files( outdir = env.from_string(outdir).render(**context) logger.debug('Copying dir %s to %s without rendering', indir, outdir) - # if the outdir is there, it must be a overwrite execution - if os.path.isdir(outdir) and overwrite_if_exists: + # The outdir is not the root dir, it is the dir which marked as copy + # only in the config file. If the program hits this line, which means + # the overwrite_if_exists = True, and root dir exists + if os.path.isdir(outdir): shutil.rmtree(outdir) shutil.copytree(indir, outdir) From c041d91782b1a7adb2ecc7a7c92ad907dc11e7f2 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 11:37:01 +0300 Subject: [PATCH 085/202] Update cookiecutter/prompt.py --- cookiecutter/prompt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index c3aa6b957..140340012 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -168,7 +168,7 @@ def render_variable(env, raw, cookiecutter_dict): def prompt_choice_for_config(cookiecutter_dict, env, key, options, no_input): """Prompt user with a set of options to choose from. - :no_input: Do not prompt for user input and return the first available option. + :param no_input: Do not prompt for user input and return the first available option. """ rendered_options = [render_variable(env, raw, cookiecutter_dict) for raw in options] if no_input: From cfad8f96a30aae0e30dfdf877a876f05c25d741b Mon Sep 17 00:00:00 2001 From: cksac Date: Mon, 6 Jun 2022 16:48:12 +0800 Subject: [PATCH 086/202] add test --- cookiecutter/generate.py | 2 +- tests/test-generate-context/nested_dict.json | 10 +++++ tests/test_generate_context.py | 42 ++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/test-generate-context/nested_dict.json diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index fcaf9f319..a20510c8d 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -68,7 +68,7 @@ def apply_overwrites_to_context(context, overwrite_context): "{} provided for choice variable {}, but the " "choices are {}.".format(overwrite, variable, context_value) ) - if isinstance(context_value, dict) and isinstance(overwrite, dict): + elif isinstance(context_value, dict) and isinstance(overwrite, dict): # Partially overwrite some keys in original dict apply_overwrites_to_context(context_value, overwrite) context[variable] = context_value diff --git a/tests/test-generate-context/nested_dict.json b/tests/test-generate-context/nested_dict.json new file mode 100644 index 000000000..c13ea10f3 --- /dev/null +++ b/tests/test-generate-context/nested_dict.json @@ -0,0 +1,10 @@ +{ + "full_name": "Raphael Pierzina", + "github_username": "hackebrot", + "project": { + "name": "Kivy Project", + "description": "Kivy Project", + "repo_name": "{{cookiecutter.project_name|lower}}", + "orientation": ["all", "landscape", "portrait"] + } +} \ No newline at end of file diff --git a/tests/test_generate_context.py b/tests/test_generate_context.py index 44ab99194..892a7588b 100644 --- a/tests/test_generate_context.py +++ b/tests/test_generate_context.py @@ -202,3 +202,45 @@ def test_apply_overwrites_sets_default_for_choice_variable(template_context): ) assert template_context['orientation'] == ['landscape', 'all', 'portrait'] + + +def test_apply_overwrites_in_nested_dict(): + """Verify nested dict in default content settings are correctly replaced.""" + expected_context = { + 'nested_dict': OrderedDict( + [ + ('full_name', 'Raphael Pierzina'), + ('github_username', 'hackebrot'), + ( + 'project', + OrderedDict( + [ + ('name', 'My Kivy Project'), + ('description', 'My Kivy Project'), + ('repo_name', '{{cookiecutter.project_name|lower}}'), + ('orientation', ["all", "landscape", "portrait"]), + ] + ), + ), + ] + ) + } + + generated_context = generate.generate_context( + context_file='tests/test-generate-context/nested_dict.json', + default_context={ + 'not_in_template': 'foobar', + 'project': { + 'description': 'My Kivy Project', + }, + }, + extra_context={ + 'also_not_in_template': 'foobar2', + 'github_username': 'hackebrot', + 'project': { + 'name': 'My Kivy Project', + }, + }, + ) + + assert generated_context == expected_context From 347ac18ccfb8a38cc76d4bab9056e3a4bb8b2e4d Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Sat, 2 Jan 2021 18:59:17 +0100 Subject: [PATCH 087/202] generate: introduces templates inheritance implement a templates directory so a maintainer can use `extends`, `includes`, `blocks`, `import` and `super` inside the cookiecutter project template using jinja directly. docs: https://jinja.palletsprojects.com/en/2.11.x/templates/#template-inheritance closes: https://github.com/cookiecutter/cookiecutter/issues/1484 ref: https://github.com/cookiecutter/cookiecutter/issues/706 ref: https://github.com/cookiecutter/cookiecutter/issues/59 ref: https://github.com/cookiecutter/cookiecutter/issues/1004 --- cookiecutter/generate.py | 2 +- docs/advanced/index.rst | 1 + docs/advanced/templates.rst | 34 +++++++++++++++ .../test-templates/extends/cookiecutter.json | 5 +++ .../extends/templates/base-requirements.jinja | 6 +++ .../templates/click-requirements.jinja | 1 + .../templates/pytest-requirements.jinja | 1 + .../requirements.txt | 1 + .../test-templates/include/cookiecutter.json | 5 +++ .../templates/click-requirements.jinja | 1 + .../templates/pytest-requirements.jinja | 1 + .../requirements.txt | 5 +++ .../no-templates/cookiecutter.json | 5 +++ .../requirements.txt | 5 +++ tests/test-templates/super/cookiecutter.json | 5 +++ .../super/templates/base-requirements.jinja | 7 ++++ .../super/templates/click-requirements.jinja | 1 + .../super/templates/pytest-requirements.jinja | 1 + .../requirements.txt | 2 + tests/test_templates.py | 41 +++++++++++++++++++ 20 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 docs/advanced/templates.rst create mode 100644 tests/test-templates/extends/cookiecutter.json create mode 100644 tests/test-templates/extends/templates/base-requirements.jinja create mode 100644 tests/test-templates/extends/templates/click-requirements.jinja create mode 100644 tests/test-templates/extends/templates/pytest-requirements.jinja create mode 100644 tests/test-templates/extends/{{cookiecutter.project_slug}}/requirements.txt create mode 100644 tests/test-templates/include/cookiecutter.json create mode 100644 tests/test-templates/include/templates/click-requirements.jinja create mode 100644 tests/test-templates/include/templates/pytest-requirements.jinja create mode 100644 tests/test-templates/include/{{cookiecutter.project_slug}}/requirements.txt create mode 100644 tests/test-templates/no-templates/cookiecutter.json create mode 100644 tests/test-templates/no-templates/{{cookiecutter.project_slug}}/requirements.txt create mode 100644 tests/test-templates/super/cookiecutter.json create mode 100644 tests/test-templates/super/templates/base-requirements.jinja create mode 100644 tests/test-templates/super/templates/click-requirements.jinja create mode 100644 tests/test-templates/super/templates/pytest-requirements.jinja create mode 100644 tests/test-templates/super/{{cookiecutter.project_slug}}/requirements.txt create mode 100644 tests/test_templates.py diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index 4b4250b7c..01cbcf8a0 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -324,7 +324,7 @@ def generate_files( ) with work_in(template_dir): - env.loader = FileSystemLoader('.') + env.loader = FileSystemLoader(['.', '../templates']) for root, dirs, files in os.walk('.'): # We must separate the two types of dirs into different lists. diff --git a/docs/advanced/index.rst b/docs/advanced/index.rst index d2111ad4b..216731d91 100644 --- a/docs/advanced/index.rst +++ b/docs/advanced/index.rst @@ -19,6 +19,7 @@ Various advanced topics regarding cookiecutter usage. replay choice_variables dict_variables + templates template_extensions directories new_line_characters diff --git a/docs/advanced/templates.rst b/docs/advanced/templates.rst new file mode 100644 index 000000000..cf670ad0e --- /dev/null +++ b/docs/advanced/templates.rst @@ -0,0 +1,34 @@ +.. _templates: + +Templates inheritance (1.X+) +--------------------------------------------------- + +*New in Cookiecutter 1.X* + +Sometimes you need to extend a base template with a different +configuration to avoid nested blocks. + +Cookiecutter introduces the ability to use common templates +using the power of jinja: `extends`, `include` and `super`. + +Here's an example repository:: + + https://github.com/user/repo-name.git + ├── {{cookiecutter.project_slug}}/ + | └── file.txt + ├── templates/ + | └── base.txt + └── cookiecutter.json + +every file in the `templates` directory will become referable inside the project itself, +and the path should be relative from the `templates` folder like :: + + # file.txt + {% extends "base.txt" %} + + ... or ... + + # file.txt + {% include "base.txt" %} + +see more on https://jinja.palletsprojects.com/en/2.11.x/templates/ diff --git a/tests/test-templates/extends/cookiecutter.json b/tests/test-templates/extends/cookiecutter.json new file mode 100644 index 000000000..e8798e956 --- /dev/null +++ b/tests/test-templates/extends/cookiecutter.json @@ -0,0 +1,5 @@ +{ + "project_slug": "foobar", + "command_line_interface": "click", + "use_pytest": "y" +} diff --git a/tests/test-templates/extends/templates/base-requirements.jinja b/tests/test-templates/extends/templates/base-requirements.jinja new file mode 100644 index 000000000..fbf1a17e7 --- /dev/null +++ b/tests/test-templates/extends/templates/base-requirements.jinja @@ -0,0 +1,6 @@ +pip==19.2.3 +{% if cookiecutter.command_line_interface|lower == 'click' -%} +{% include 'click-requirements.jinja' %}{% endif %} +{% if cookiecutter.use_pytest == 'y' -%} +{% include 'pytest-requirements.jinja' %}{% endif %} +{% block dependencies %}{% endblock %} \ No newline at end of file diff --git a/tests/test-templates/extends/templates/click-requirements.jinja b/tests/test-templates/extends/templates/click-requirements.jinja new file mode 100644 index 000000000..8e1cde652 --- /dev/null +++ b/tests/test-templates/extends/templates/click-requirements.jinja @@ -0,0 +1 @@ +Click==7.0 \ No newline at end of file diff --git a/tests/test-templates/extends/templates/pytest-requirements.jinja b/tests/test-templates/extends/templates/pytest-requirements.jinja new file mode 100644 index 000000000..18a0e4267 --- /dev/null +++ b/tests/test-templates/extends/templates/pytest-requirements.jinja @@ -0,0 +1 @@ +pytest==4.6.5 \ No newline at end of file diff --git a/tests/test-templates/extends/{{cookiecutter.project_slug}}/requirements.txt b/tests/test-templates/extends/{{cookiecutter.project_slug}}/requirements.txt new file mode 100644 index 000000000..910e3721e --- /dev/null +++ b/tests/test-templates/extends/{{cookiecutter.project_slug}}/requirements.txt @@ -0,0 +1 @@ +{% extends "base-requirements.jinja" %} \ No newline at end of file diff --git a/tests/test-templates/include/cookiecutter.json b/tests/test-templates/include/cookiecutter.json new file mode 100644 index 000000000..e8798e956 --- /dev/null +++ b/tests/test-templates/include/cookiecutter.json @@ -0,0 +1,5 @@ +{ + "project_slug": "foobar", + "command_line_interface": "click", + "use_pytest": "y" +} diff --git a/tests/test-templates/include/templates/click-requirements.jinja b/tests/test-templates/include/templates/click-requirements.jinja new file mode 100644 index 000000000..8e1cde652 --- /dev/null +++ b/tests/test-templates/include/templates/click-requirements.jinja @@ -0,0 +1 @@ +Click==7.0 \ No newline at end of file diff --git a/tests/test-templates/include/templates/pytest-requirements.jinja b/tests/test-templates/include/templates/pytest-requirements.jinja new file mode 100644 index 000000000..18a0e4267 --- /dev/null +++ b/tests/test-templates/include/templates/pytest-requirements.jinja @@ -0,0 +1 @@ +pytest==4.6.5 \ No newline at end of file diff --git a/tests/test-templates/include/{{cookiecutter.project_slug}}/requirements.txt b/tests/test-templates/include/{{cookiecutter.project_slug}}/requirements.txt new file mode 100644 index 000000000..4ea7b13f6 --- /dev/null +++ b/tests/test-templates/include/{{cookiecutter.project_slug}}/requirements.txt @@ -0,0 +1,5 @@ +pip==19.2.3 +{% if cookiecutter.command_line_interface|lower == 'click' -%} +{% include 'click-requirements.jinja' %}{% endif %} +{% if cookiecutter.use_pytest == 'y' -%} +{% include 'pytest-requirements.jinja' %}{% endif %} \ No newline at end of file diff --git a/tests/test-templates/no-templates/cookiecutter.json b/tests/test-templates/no-templates/cookiecutter.json new file mode 100644 index 000000000..e8798e956 --- /dev/null +++ b/tests/test-templates/no-templates/cookiecutter.json @@ -0,0 +1,5 @@ +{ + "project_slug": "foobar", + "command_line_interface": "click", + "use_pytest": "y" +} diff --git a/tests/test-templates/no-templates/{{cookiecutter.project_slug}}/requirements.txt b/tests/test-templates/no-templates/{{cookiecutter.project_slug}}/requirements.txt new file mode 100644 index 000000000..6554e75ff --- /dev/null +++ b/tests/test-templates/no-templates/{{cookiecutter.project_slug}}/requirements.txt @@ -0,0 +1,5 @@ +pip==19.2.3 +{% if cookiecutter.command_line_interface|lower == 'click' -%} +Click==7.0{% endif %} +{% if cookiecutter.use_pytest == 'y' -%} +pytest==4.6.5{% endif %} \ No newline at end of file diff --git a/tests/test-templates/super/cookiecutter.json b/tests/test-templates/super/cookiecutter.json new file mode 100644 index 000000000..e8798e956 --- /dev/null +++ b/tests/test-templates/super/cookiecutter.json @@ -0,0 +1,5 @@ +{ + "project_slug": "foobar", + "command_line_interface": "click", + "use_pytest": "y" +} diff --git a/tests/test-templates/super/templates/base-requirements.jinja b/tests/test-templates/super/templates/base-requirements.jinja new file mode 100644 index 000000000..c4fd8141e --- /dev/null +++ b/tests/test-templates/super/templates/base-requirements.jinja @@ -0,0 +1,7 @@ +pip==19.2.3 +{% if cookiecutter.command_line_interface|lower == 'click' -%} +{% include 'click-requirements.jinja' %}{% endif %} +{%- block dev_dependencies %} +{% if cookiecutter.use_pytest == 'y' -%}{% include 'pytest-requirements.jinja' %}{% endif %} +{%- endblock %} +{% block dependencies %}{% endblock %} \ No newline at end of file diff --git a/tests/test-templates/super/templates/click-requirements.jinja b/tests/test-templates/super/templates/click-requirements.jinja new file mode 100644 index 000000000..8e1cde652 --- /dev/null +++ b/tests/test-templates/super/templates/click-requirements.jinja @@ -0,0 +1 @@ +Click==7.0 \ No newline at end of file diff --git a/tests/test-templates/super/templates/pytest-requirements.jinja b/tests/test-templates/super/templates/pytest-requirements.jinja new file mode 100644 index 000000000..18a0e4267 --- /dev/null +++ b/tests/test-templates/super/templates/pytest-requirements.jinja @@ -0,0 +1 @@ +pytest==4.6.5 \ No newline at end of file diff --git a/tests/test-templates/super/{{cookiecutter.project_slug}}/requirements.txt b/tests/test-templates/super/{{cookiecutter.project_slug}}/requirements.txt new file mode 100644 index 000000000..602b5772f --- /dev/null +++ b/tests/test-templates/super/{{cookiecutter.project_slug}}/requirements.txt @@ -0,0 +1,2 @@ +{% extends "base-requirements.jinja" %} +{% block dev_dependencies %}{{ super() }}{% endblock %} \ No newline at end of file diff --git a/tests/test_templates.py b/tests/test_templates.py new file mode 100644 index 000000000..a981c81ab --- /dev/null +++ b/tests/test_templates.py @@ -0,0 +1,41 @@ +""" +test_custom_extension_in_hooks. + +Tests to ensure custom cookiecutter extensions are properly made available to +pre- and post-gen hooks. +""" +import codecs +import os + +import pytest + +from cookiecutter import main + + +@pytest.fixture +def output_dir(tmpdir): + """Fixture. Create and return custom temp directory for test.""" + return str(tmpdir.mkdir('templates')) + + +@pytest.mark.parametrize("template", ["include", "no-templates", "extends", "super"]) +def test_build_templates(template, output_dir): + """ + Verify Templates Design keywords. + + no-templates is a compatibility tests for repo without `templates` directory + """ + project_dir = main.cookiecutter( + f'tests/test-templates/{template}', no_input=True, output_dir=output_dir, + ) + + readme_file = os.path.join(project_dir, 'requirements.txt') + + with codecs.open(readme_file, encoding='utf8') as f: + readme = f.read().splitlines() + + assert readme == [ + "pip==19.2.3", + "Click==7.0", + "pytest==4.6.5", + ] From 13526907ba9aa8c97604d1c0db055128c7a1d58f Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 12:51:46 +0300 Subject: [PATCH 088/202] Fix linting in #1485 --- tests/test_templates.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_templates.py b/tests/test_templates.py index a981c81ab..70cf76b5a 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -26,7 +26,9 @@ def test_build_templates(template, output_dir): no-templates is a compatibility tests for repo without `templates` directory """ project_dir = main.cookiecutter( - f'tests/test-templates/{template}', no_input=True, output_dir=output_dir, + f'tests/test-templates/{template}', + no_input=True, + output_dir=output_dir, ) readme_file = os.path.join(project_dir, 'requirements.txt') From 012d04a51922e5670b01ccf9d9dcd0c13a8e5178 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 13:12:47 +0300 Subject: [PATCH 089/202] Update docs/advanced/templates.rst --- docs/advanced/templates.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/templates.rst b/docs/advanced/templates.rst index cf670ad0e..f297357c1 100644 --- a/docs/advanced/templates.rst +++ b/docs/advanced/templates.rst @@ -1,6 +1,6 @@ .. _templates: -Templates inheritance (1.X+) +Templates inheritance (2.2+) --------------------------------------------------- *New in Cookiecutter 1.X* From d269707f8f96e450cff37a1eb6bfe6284029cb8a Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 13:12:53 +0300 Subject: [PATCH 090/202] Update docs/advanced/templates.rst --- docs/advanced/templates.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/templates.rst b/docs/advanced/templates.rst index f297357c1..5113bf940 100644 --- a/docs/advanced/templates.rst +++ b/docs/advanced/templates.rst @@ -3,7 +3,7 @@ Templates inheritance (2.2+) --------------------------------------------------- -*New in Cookiecutter 1.X* +*New in Cookiecutter 2.2+* Sometimes you need to extend a base template with a different configuration to avoid nested blocks. From 185feb10974796aaebd3d2eb40c0c5e7a9f494d8 Mon Sep 17 00:00:00 2001 From: liortct Date: Mon, 20 Dec 2021 00:17:22 +0200 Subject: [PATCH 091/202] Add boolean variable support --- cookiecutter/prompt.py | 8 +++++ docs/advanced/boolean_variables.rst | 46 +++++++++++++++++++++++++++++ docs/advanced/index.rst | 1 + tests/test_prompt.py | 44 ++++++++++++++++++++++++--- 4 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 docs/advanced/boolean_variables.rst diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index a067d0932..4e32d30f9 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -145,6 +145,8 @@ def render_variable(env, raw, cookiecutter_dict): """ if raw is None: return None + elif isinstance(raw, bool): + return raw elif isinstance(raw, dict): return { render_variable(env, k, cookiecutter_dict): render_variable( @@ -201,6 +203,12 @@ def prompt_for_config(context, no_input=False): cookiecutter_dict, env, key, raw, no_input ) cookiecutter_dict[key] = val + elif isinstance(raw, bool): + # We are dealing with a boolean variable + val = render_variable(env, raw, cookiecutter_dict) + if not no_input: + val = read_user_yes_no(key, raw) + cookiecutter_dict[key] = val elif not isinstance(raw, dict): # We are dealing with a regular variable val = render_variable(env, raw, cookiecutter_dict) diff --git a/docs/advanced/boolean_variables.rst b/docs/advanced/boolean_variables.rst new file mode 100644 index 000000000..3016c8ae7 --- /dev/null +++ b/docs/advanced/boolean_variables.rst @@ -0,0 +1,46 @@ +.. _boolean-variables: + +Boolean Variables (2.0+) +------------------------ + +Boolean variables are used for answering True/False questions. + +Basic Usage +~~~~~~~~~~~ + +Boolean variables are regular key / value pairs, but with the value being True/False. + +For example, if you provide the following boolean variable in your ``cookiecutter.json``:: + + { + "run_as_docker": true + } + +you'd get the following user input when running Cookiecutter:: + + run_as_docker [True]: + +Depending on the user's input, a different condition will be selected. + +The above ``run_as_docker`` boolean variable creates ``cookiecutter.run_as_docker``, which +can be used like this:: + + {%- if cookiecutter.run_as_docker -%} + # In case of True add your content here + + {%- else -%} + # In case of False add your content here + + {% endif %} + +Cookiecutter is using `Jinja2's if conditional expression `_ to determine the correct ``run_as_docker``. + +Input Validation +~~~~~~~~~~~~~~~~ +If a non valid value is inserted to a boolean field, the following error will be printed: + +.. code-block:: bash + + run_as_docker [True]: docker + Error: docker is not a valid boolean + diff --git a/docs/advanced/index.rst b/docs/advanced/index.rst index 216731d91..89150af5d 100644 --- a/docs/advanced/index.rst +++ b/docs/advanced/index.rst @@ -18,6 +18,7 @@ Various advanced topics regarding cookiecutter usage. copy_without_render replay choice_variables + boolean_variables dict_variables templates template_extensions diff --git a/tests/test_prompt.py b/tests/test_prompt.py index 037591dd4..9e85bcd9e 100644 --- a/tests/test_prompt.py +++ b/tests/test_prompt.py @@ -21,7 +21,7 @@ class TestRenderVariable: 'raw_var, rendered_var', [ (1, '1'), - (True, 'True'), + (True, True), ('foo', 'foo'), ('{{cookiecutter.project}}', 'foobar'), (None, None), @@ -39,7 +39,7 @@ def test_convert_to_str(self, mocker, raw_var, rendered_var): assert result == rendered_var # Make sure that non None non str variables are converted beforehand - if raw_var is not None: + if raw_var is not None and not isinstance(raw_var, bool): if not isinstance(raw_var, str): raw_var = str(raw_var) from_string.assert_called_once_with(raw_var) @@ -49,10 +49,10 @@ def test_convert_to_str(self, mocker, raw_var, rendered_var): @pytest.mark.parametrize( 'raw_var, rendered_var', [ - ({1: True, 'foo': False}, {'1': 'True', 'foo': 'False'}), + ({1: True, 'foo': False}, {'1': True, 'foo': False}), ( {'{{cookiecutter.project}}': ['foo', 1], 'bar': False}, - {'foobar': ['foo', '1'], 'bar': 'False'}, + {'foobar': ['foo', '1'], 'bar': False}, ), (['foo', '{{cookiecutter.project}}', None], ['foo', 'foobar', None]), ], @@ -380,6 +380,42 @@ def test_should_read_user_choice(self, mocker, choices, context): assert expected_choice == actual_choice +class TestReadUserYesNo(object): + """Class to unite boolean prompt related tests.""" + + @pytest.mark.parametrize( + 'run_as_docker', + ( + True, + False, + ), + ) + def test_should_invoke_read_user_yes_no(self, mocker, run_as_docker): + """Verify correct function called for boolean variables.""" + read_user_yes_no = mocker.patch('cookiecutter.prompt.read_user_yes_no') + read_user_yes_no.return_value = run_as_docker + + read_user_variable = mocker.patch('cookiecutter.prompt.read_user_variable') + + context = {'cookiecutter': {'run_as_docker': run_as_docker}} + + cookiecutter_dict = prompt.prompt_for_config(context) + + assert not read_user_variable.called + read_user_yes_no.assert_called_once_with('run_as_docker', run_as_docker) + assert cookiecutter_dict == {'run_as_docker': run_as_docker} + + def test_boolean_parameter_no_input(self): + """Verify boolean parameter sent to prompt for config with no input.""" + context = { + 'cookiecutter': { + 'run_as_docker': True, + } + } + cookiecutter_dict = prompt.prompt_for_config(context, no_input=True) + assert cookiecutter_dict == context['cookiecutter'] + + @pytest.mark.parametrize( 'context', ( From 789d6d045a393bc770287ae193edb25aaf84a4ad Mon Sep 17 00:00:00 2001 From: liortct Date: Mon, 20 Dec 2021 00:56:27 +0200 Subject: [PATCH 092/202] Fix code to follow Code Climate suggestions --- cookiecutter/prompt.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index 4e32d30f9..003ace67d 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -143,9 +143,7 @@ def render_variable(env, raw, cookiecutter_dict): being populated with variables. :return: The rendered value for the default variable. """ - if raw is None: - return None - elif isinstance(raw, bool): + if raw is None or isinstance(raw, bool): return raw elif isinstance(raw, dict): return { @@ -205,10 +203,12 @@ def prompt_for_config(context, no_input=False): cookiecutter_dict[key] = val elif isinstance(raw, bool): # We are dealing with a boolean variable - val = render_variable(env, raw, cookiecutter_dict) - if not no_input: - val = read_user_yes_no(key, raw) - cookiecutter_dict[key] = val + if no_input: + cookiecutter_dict[key] = render_variable( + env, raw, cookiecutter_dict + ) + else: + cookiecutter_dict[key] = read_user_yes_no(key, raw) elif not isinstance(raw, dict): # We are dealing with a regular variable val = render_variable(env, raw, cookiecutter_dict) From 768bfcf6eeeb29d896718066c7d47c85f2c6a393 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 13:34:56 +0300 Subject: [PATCH 093/202] Update docs/advanced/boolean_variables.rst --- docs/advanced/boolean_variables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/boolean_variables.rst b/docs/advanced/boolean_variables.rst index 3016c8ae7..58c5e48a7 100644 --- a/docs/advanced/boolean_variables.rst +++ b/docs/advanced/boolean_variables.rst @@ -1,6 +1,6 @@ .. _boolean-variables: -Boolean Variables (2.0+) +Boolean Variables (2.2+) ------------------------ Boolean variables are used for answering True/False questions. From ec58a7176a95f77b676fb6a5df0f2aeebd257df6 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 22:12:47 +0300 Subject: [PATCH 094/202] Fix release-drafter template typo. Closes #1699 --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 6eb8aeb13..c25ca352b 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -32,6 +32,6 @@ template: | $CHANGES - ## This release is made by wonderfull contributors: + ## This release is made by wonderful contributors: $CONTRIBUTORS From d5d212070f53047cea9d1d29e5976ecfe813d429 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 22:20:55 +0300 Subject: [PATCH 095/202] Add autolabeler support for workflow --- .github/workflows/release-drafter.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 17fdb961d..c7cd0bbcc 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -4,9 +4,18 @@ on: push: branches: - master + # autolabeler + pull_request: + types: + - opened + - reopened + - synchronize jobs: update_release_draft: + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: - uses: release-drafter/release-drafter@v5 From 902b3cb63806629c62e44a4be178af61a82b71f8 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 22:28:01 +0300 Subject: [PATCH 096/202] Add version resolver to release-drafter --- .github/release-drafter.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index c25ca352b..ced9f1fa8 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,5 +1,25 @@ filter-by-commitish: true commitish: master +name-template: '$RESOLVED_VERSION' +tag-template: '$RESOLVED_VERSION' +version-resolver: + major: + labels: + - 'breaking-change' + - 'major' + minor: + labels: + - 'enhancement' + - 'feature' + patch: + labels: + - 'bug' + - 'CI/CD' + - 'code style' + - 'documentation' + - 'tests' + - 'patch' + default: patch categories: - title: 'Breaking Changes' labels: From 084a03bbd660f8b407d0c619e694488c31c8515b Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 22:58:14 +0300 Subject: [PATCH 097/202] Add autolabeler to release-drafter --- .github/release-drafter.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index ced9f1fa8..1cda0440c 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -20,6 +20,23 @@ version-resolver: - 'tests' - 'patch' default: patch +autolabeler: + - label: 'CI/CD' + files: + - '.github/*' + - '.pre-commit-config.yaml' + - '*.cfg' + - '*.ini' + - 'noxfile.py' + - 'setup.*' + - 'docs/conf.py' + - 'Makefile' + - 'make.bat' + - '*requirements*.txt' + - label: 'documentation' + files: + - '*.md' + - '*.rst' categories: - title: 'Breaking Changes' labels: From a1a61e3285030b106e46163231520a0e77269dd3 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 23:00:25 +0300 Subject: [PATCH 098/202] Configure notes sorting in release-drafter --- .github/release-drafter.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 1cda0440c..13ec354d8 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -62,6 +62,8 @@ categories: - title: 'Deprecations' labels: - 'deprecated' +sort-by: title +sort-direction: ascending exclude-labels: - 'skip-changelog' template: | From 5f6aaee3af0b0c192a314962f76ab8902fd405cb Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 01:43:17 +0300 Subject: [PATCH 099/202] Extend tests requirements --- test_requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_requirements.txt b/test_requirements.txt index 4607cefe1..febcb2ae3 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -2,3 +2,5 @@ pytest pytest-cov pytest-mock freezegun +safety +pre-commit From 018705635d3746317923c09b296b3293db23df82 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 01:48:41 +0300 Subject: [PATCH 100/202] Add nox configuration --- MANIFEST.in | 1 + noxfile.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 noxfile.py diff --git a/MANIFEST.in b/MANIFEST.in index 0e1034643..4e36e27df 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -11,6 +11,7 @@ exclude .* exclude codecov.yml exclude test_requirements.txt exclude tox.ini +exclude noxfile.py recursive-include tests * recursive-exclude * __pycache__ diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 000000000..d2789664b --- /dev/null +++ b/noxfile.py @@ -0,0 +1,73 @@ +"""Nox tool configuration file. + +Nox is Tox tool replacement. +""" +import nox + +nox.options.keywords = "not docs" + + +def base_install(session): + """Create basic environment setup for tests and linting.""" + session.install("-r", "test_requirements.txt") + session.install("-e", ".") + return session + + +@nox.session(python="3.10") +def lint(session): + """Run linting check locally.""" + session = base_install(session) + session.run("pre-commit", "run", "-a") + + +@nox.session(python=["3.7", "3.8", "3.9", "3.10"]) +def tests(session): + """Run test suite with pytest.""" + session = base_install(session) + session.run( + "pytest", + "--cov-report=html", + "--cov-report=xml", + "--cov-branch", + "--cov-fail-under=100", + ) + + +@nox.session(python=["3.7", "3.8", "3.9", "3.10"]) +def safety_tests(session): + """Run safety tests.""" + session = base_install(session) + session.run("safety", "check", "--full-report") + + +@nox.session(python="3.10") +def documentation_tests(session): + """Run documentation tests.""" + return docs(session, batch_run=True) + + +@nox.session(python="3.10") +def docs(session, batch_run: bool = False): + """Build the documentation or serve documentation interactively.""" + session.run("rm", "-rf", "docs/_build", external=True) + session.install("-r", "docs/requirements.txt") + session.install(".") + session.cd("docs") + sphinx_args = ["-b", "html", "-W", ".", "_build/html"] + + if not session.interactive or batch_run: + sphinx_cmd = "sphinx-build" + else: + sphinx_cmd = "sphinx-autobuild" + sphinx_args.extend( + [ + "--open-browser", + "--port", + "9812", + "--watch", + "../cookiecutter", + ] + ) + + session.run(sphinx_cmd, *sphinx_args) From aa951c1716e8da41c71de78b914df9217dd01df5 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 01:49:10 +0300 Subject: [PATCH 101/202] Fix codeblock style (failed docs build) --- docs/advanced/suppressing_prompts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/suppressing_prompts.rst b/docs/advanced/suppressing_prompts.rst index be8a549fd..68f4a292e 100644 --- a/docs/advanced/suppressing_prompts.rst +++ b/docs/advanced/suppressing_prompts.rst @@ -30,7 +30,7 @@ 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: -.. code-block:: JSON +.. code-block:: python cookiecutter('cookiecutter-pypackage/', no_input=True, From 59bfbb3dfd0c1dc6929ad46613e90b2b12a62c55 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 01:49:52 +0300 Subject: [PATCH 102/202] Add sphinx-autobuild for nox correct run #1698 --- docs/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index 40157c3ae..5b5407b85 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,3 +2,4 @@ watchdog>=0.10.2 sphinx-rtd-theme>=0.4.3 sphinx-click>=2.3.2 recommonmark>=0.6.0 +sphinx-autobuild>=2021.3.14 \ No newline at end of file From 47d2341a5d4baa5f99c9f8a0d2a171453fca8224 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 02:20:17 +0300 Subject: [PATCH 103/202] Added readthedocs build config --- .readthedocs.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..6c06c6854 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,19 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details +version: 2 +build: + os: ubuntu-20.04 + tools: + python: "3.10" +sphinx: + configuration: docs/conf.py +formats: + - htmlzip + - pdf + - epub +python: + install: + - requirements: docs/requirements.txt + - method: pip + path: . \ No newline at end of file From d177d33fa2414f834c88978f7fc5b6446ffdd7a0 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 02:34:16 +0300 Subject: [PATCH 104/202] Use latest ubuntu for builds --- .readthedocs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 6c06c6854..988f6ffbf 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -3,7 +3,7 @@ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details version: 2 build: - os: ubuntu-20.04 + os: ubuntu-22.04 tools: python: "3.10" sphinx: @@ -16,4 +16,4 @@ python: install: - requirements: docs/requirements.txt - method: pip - path: . \ No newline at end of file + path: . From 7c7d39a8444972853c26a32a51c8fb3f3632dc1b Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 03:44:20 +0300 Subject: [PATCH 105/202] Update sphinx requirements --- docs/requirements.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 5b5407b85..14510c6a8 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,6 @@ watchdog>=0.10.2 -sphinx-rtd-theme>=0.4.3 -sphinx-click>=2.3.2 -recommonmark>=0.6.0 -sphinx-autobuild>=2021.3.14 \ No newline at end of file +sphinx-rtd-theme>=1.0.0 +sphinx-click>=4.1.0 +myst-parser>=0.17.2 +sphinx-autobuild>=2021.3.14 +Sphinx>=4.0.1 From c8fbb3192b76a092b463b4842cfa75b4aea0c4a5 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 03:44:35 +0300 Subject: [PATCH 106/202] Fix myst broken links --- CONTRIBUTING.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0ae0bb42..283506e9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,11 +3,11 @@ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. -- [Types of Contributions](#Types-of-Contributions) -- [Contributor Setup](#Setting-Up-the-Code-for-Local-Development) -- [Contributor Guidelines](#Contributor-Guidelines) -- [Contributor Testing](#Testing-with-tox) -- [Core Committer Guide](#Core-Committer-Guide) +- [Types of Contributions](#types-of-contributions) +- [Contributor Setup](#setting-up-the-code-for-local-development) +- [Contributor Guidelines](#contributor-guidelines) +- [Contributor Testing](#testing-with-tox) +- [Core Committer Guide](#core-committer-guide) ## Types of Contributions From 30c691e4afe68e28e7134d3b1571297aa82759b1 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 03:53:58 +0300 Subject: [PATCH 107/202] Update sphinx configuration --- docs/conf.py | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 51648bd10..cb4be1464 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,31 +28,6 @@ # flake8: noqa D107,D105 - -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) @@ -78,7 +53,7 @@ def __getattr__(cls, name): 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'sphinx_click.ext', - 'recommonmark', + 'myst_parser', ] # Add any paths that contain templates here, relative to this directory. @@ -378,3 +353,9 @@ def __getattr__(cls, name): # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'https://docs.python.org/3': None} +myst_enable_extensions = [ + "tasklist", + "strikethrough", + "fieldlist", +] +myst_heading_anchors = 3 From 9d445f4ca46996de47f774bed737888c326eccfc Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 05:24:27 +0300 Subject: [PATCH 108/202] remove versions from template, to exclude dependabot warnings --- .../extends/templates/base-requirements.jinja | 2 +- .../extends/templates/click-requirements.jinja | 2 +- .../extends/templates/pytest-requirements.jinja | 2 +- .../include/templates/click-requirements.jinja | 2 +- .../include/templates/pytest-requirements.jinja | 2 +- .../include/{{cookiecutter.project_slug}}/requirements.txt | 2 +- .../{{cookiecutter.project_slug}}/requirements.txt | 6 +++--- .../test-templates/super/templates/base-requirements.jinja | 2 +- .../test-templates/super/templates/click-requirements.jinja | 2 +- .../super/templates/pytest-requirements.jinja | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test-templates/extends/templates/base-requirements.jinja b/tests/test-templates/extends/templates/base-requirements.jinja index fbf1a17e7..964fa3379 100644 --- a/tests/test-templates/extends/templates/base-requirements.jinja +++ b/tests/test-templates/extends/templates/base-requirements.jinja @@ -1,4 +1,4 @@ -pip==19.2.3 +pip {% if cookiecutter.command_line_interface|lower == 'click' -%} {% include 'click-requirements.jinja' %}{% endif %} {% if cookiecutter.use_pytest == 'y' -%} diff --git a/tests/test-templates/extends/templates/click-requirements.jinja b/tests/test-templates/extends/templates/click-requirements.jinja index 8e1cde652..4d441badc 100644 --- a/tests/test-templates/extends/templates/click-requirements.jinja +++ b/tests/test-templates/extends/templates/click-requirements.jinja @@ -1 +1 @@ -Click==7.0 \ No newline at end of file +Click \ No newline at end of file diff --git a/tests/test-templates/extends/templates/pytest-requirements.jinja b/tests/test-templates/extends/templates/pytest-requirements.jinja index 18a0e4267..55b033e90 100644 --- a/tests/test-templates/extends/templates/pytest-requirements.jinja +++ b/tests/test-templates/extends/templates/pytest-requirements.jinja @@ -1 +1 @@ -pytest==4.6.5 \ No newline at end of file +pytest \ No newline at end of file diff --git a/tests/test-templates/include/templates/click-requirements.jinja b/tests/test-templates/include/templates/click-requirements.jinja index 8e1cde652..4d441badc 100644 --- a/tests/test-templates/include/templates/click-requirements.jinja +++ b/tests/test-templates/include/templates/click-requirements.jinja @@ -1 +1 @@ -Click==7.0 \ No newline at end of file +Click \ No newline at end of file diff --git a/tests/test-templates/include/templates/pytest-requirements.jinja b/tests/test-templates/include/templates/pytest-requirements.jinja index 18a0e4267..55b033e90 100644 --- a/tests/test-templates/include/templates/pytest-requirements.jinja +++ b/tests/test-templates/include/templates/pytest-requirements.jinja @@ -1 +1 @@ -pytest==4.6.5 \ No newline at end of file +pytest \ No newline at end of file diff --git a/tests/test-templates/include/{{cookiecutter.project_slug}}/requirements.txt b/tests/test-templates/include/{{cookiecutter.project_slug}}/requirements.txt index 4ea7b13f6..62645fd16 100644 --- a/tests/test-templates/include/{{cookiecutter.project_slug}}/requirements.txt +++ b/tests/test-templates/include/{{cookiecutter.project_slug}}/requirements.txt @@ -1,4 +1,4 @@ -pip==19.2.3 +pip {% if cookiecutter.command_line_interface|lower == 'click' -%} {% include 'click-requirements.jinja' %}{% endif %} {% if cookiecutter.use_pytest == 'y' -%} diff --git a/tests/test-templates/no-templates/{{cookiecutter.project_slug}}/requirements.txt b/tests/test-templates/no-templates/{{cookiecutter.project_slug}}/requirements.txt index 6554e75ff..b8b92ac2b 100644 --- a/tests/test-templates/no-templates/{{cookiecutter.project_slug}}/requirements.txt +++ b/tests/test-templates/no-templates/{{cookiecutter.project_slug}}/requirements.txt @@ -1,5 +1,5 @@ -pip==19.2.3 +pip {% if cookiecutter.command_line_interface|lower == 'click' -%} -Click==7.0{% endif %} +Click{% endif %} {% if cookiecutter.use_pytest == 'y' -%} -pytest==4.6.5{% endif %} \ No newline at end of file +pytest{% endif %} \ No newline at end of file diff --git a/tests/test-templates/super/templates/base-requirements.jinja b/tests/test-templates/super/templates/base-requirements.jinja index c4fd8141e..23c1ca511 100644 --- a/tests/test-templates/super/templates/base-requirements.jinja +++ b/tests/test-templates/super/templates/base-requirements.jinja @@ -1,4 +1,4 @@ -pip==19.2.3 +pip {% if cookiecutter.command_line_interface|lower == 'click' -%} {% include 'click-requirements.jinja' %}{% endif %} {%- block dev_dependencies %} diff --git a/tests/test-templates/super/templates/click-requirements.jinja b/tests/test-templates/super/templates/click-requirements.jinja index 8e1cde652..4d441badc 100644 --- a/tests/test-templates/super/templates/click-requirements.jinja +++ b/tests/test-templates/super/templates/click-requirements.jinja @@ -1 +1 @@ -Click==7.0 \ No newline at end of file +Click \ No newline at end of file diff --git a/tests/test-templates/super/templates/pytest-requirements.jinja b/tests/test-templates/super/templates/pytest-requirements.jinja index 18a0e4267..55b033e90 100644 --- a/tests/test-templates/super/templates/pytest-requirements.jinja +++ b/tests/test-templates/super/templates/pytest-requirements.jinja @@ -1 +1 @@ -pytest==4.6.5 \ No newline at end of file +pytest \ No newline at end of file From 0610b651bfd0bb10a4f1f3f2be254315a65c792e Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 05:24:43 +0300 Subject: [PATCH 109/202] Exclude versions from tests --- tests/test_templates.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_templates.py b/tests/test_templates.py index 70cf76b5a..27f40428b 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -37,7 +37,7 @@ def test_build_templates(template, output_dir): readme = f.read().splitlines() assert readme == [ - "pip==19.2.3", - "Click==7.0", - "pytest==4.6.5", + "pip", + "Click", + "pytest", ] From a11e8153bfbdc98d6ceb47423ebb2ac469474280 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 05:59:07 +0300 Subject: [PATCH 110/202] Update pre-commit config --- .pre-commit-config.yaml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7be7e3bb8..96d89ad9a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ --- repos: - repo: https://github.com/PyCQA/doc8 - rev: 0.8.1 + rev: 0.11.2 hooks: - id: doc8 name: doc8 @@ -17,17 +17,26 @@ repos: language_version: python3 exclude: ^(tests\/hooks-abort-render\/hooks|docs\/HelloCookieCutter1) - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.4.0 + rev: v4.2.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - id: mixed-line-ending - - id: check-byte-order-marker + - id: fix-byte-order-marker - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable - id: check-merge-conflict - id: check-symlinks + - id: check-case-conflict + - id: check-docstring-first + - id: check-json + exclude: "invalid-syntax.json|tests/fake-repo-bad-json/cookiecutter.json|tests/fake-repo/cookiecutter.json" + - id: check-toml + - id: check-xml + - id: check-yaml + exclude: "not_rendered.yml|invalid-config.yaml" - repo: https://gitlab.com/pycqa/flake8 - rev: 3.7.9 + rev: 4.0.1 hooks: - id: flake8 additional_dependencies: @@ -35,7 +44,7 @@ repos: - flake8-black - flake8-docstrings - repo: https://github.com/PyCQA/bandit - rev: 1.6.0 + rev: 1.7.4 hooks: - id: bandit args: [--ini, .bandit] From 6fdbb92e0793015e35e43a8cf53de4991283cf8b Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 05:59:21 +0300 Subject: [PATCH 111/202] Remove unneeded shebangs to fix pre-commit issues --- setup.py | 1 - tests/hooks-abort-render/hooks/post_gen_project.py | 1 - tests/hooks-abort-render/hooks/pre_gen_project.py | 1 - tests/test-pyhooks/hooks/post_gen_project.py | 1 - tests/test-pyhooks/hooks/pre_gen_project.py | 1 - tests/test-pyshellhooks/hooks/post_gen_project.py | 1 - tests/test-pyshellhooks/hooks/pre_gen_project.py | 1 - 7 files changed, 7 deletions(-) diff --git a/setup.py b/setup.py index 7c6a677b2..d61fbd6ba 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """cookiecutter distutils configuration.""" from setuptools import setup diff --git a/tests/hooks-abort-render/hooks/post_gen_project.py b/tests/hooks-abort-render/hooks/post_gen_project.py index 706cc440d..d95ca59fa 100644 --- a/tests/hooks-abort-render/hooks/post_gen_project.py +++ b/tests/hooks-abort-render/hooks/post_gen_project.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # flake8: noqa """Simple post-gen hook for testing the handling of different exit codes.""" diff --git a/tests/hooks-abort-render/hooks/pre_gen_project.py b/tests/hooks-abort-render/hooks/pre_gen_project.py index a132af807..3bd59868c 100644 --- a/tests/hooks-abort-render/hooks/pre_gen_project.py +++ b/tests/hooks-abort-render/hooks/pre_gen_project.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # flake8: noqa """Simple pre-gen hook for testing the handling of different exit codes.""" diff --git a/tests/test-pyhooks/hooks/post_gen_project.py b/tests/test-pyhooks/hooks/post_gen_project.py index c8b7c194f..98a5a353b 100644 --- a/tests/test-pyhooks/hooks/post_gen_project.py +++ b/tests/test-pyhooks/hooks/post_gen_project.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """Simple post-gen hook for testing project folder and custom file creation.""" print('pre generation hook') diff --git a/tests/test-pyhooks/hooks/pre_gen_project.py b/tests/test-pyhooks/hooks/pre_gen_project.py index 4d84bd3ec..6f1887bd4 100644 --- a/tests/test-pyhooks/hooks/pre_gen_project.py +++ b/tests/test-pyhooks/hooks/pre_gen_project.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """Simple pre-gen hook for testing project folder and custom file creation.""" print('pre generation hook') diff --git a/tests/test-pyshellhooks/hooks/post_gen_project.py b/tests/test-pyshellhooks/hooks/post_gen_project.py index c8b7c194f..98a5a353b 100644 --- a/tests/test-pyshellhooks/hooks/post_gen_project.py +++ b/tests/test-pyshellhooks/hooks/post_gen_project.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """Simple post-gen hook for testing project folder and custom file creation.""" print('pre generation hook') diff --git a/tests/test-pyshellhooks/hooks/pre_gen_project.py b/tests/test-pyshellhooks/hooks/pre_gen_project.py index db8bfc6a7..daeb59acb 100644 --- a/tests/test-pyshellhooks/hooks/pre_gen_project.py +++ b/tests/test-pyshellhooks/hooks/pre_gen_project.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """Simple pre-gen hook for testing project folder and custom file creation.""" From 7efd07b49a77715e30e06dee1795690c0b04408f Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 07:13:55 +0300 Subject: [PATCH 112/202] Remove unused sphinx files --- docs/Makefile | 177 ------------------------------------ docs/make.bat | 242 -------------------------------------------------- 2 files changed, 419 deletions(-) delete mode 100644 docs/Makefile delete mode 100644 docs/make.bat diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 01325d08b..000000000 --- a/docs/Makefile +++ /dev/null @@ -1,177 +0,0 @@ -# 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." diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 908517ffa..000000000 --- a/docs/make.bat +++ /dev/null @@ -1,242 +0,0 @@ -@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 From 94631a846035a24ba20d233a5c9347f4cf0b6b1e Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 09:02:31 +0300 Subject: [PATCH 113/202] Add nox files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8c96562ec..24aec0102 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache From ac047dc48075eed678ec08f59526a6759f6cc305 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 09:03:53 +0300 Subject: [PATCH 114/202] Replace not windows supported sed with nox workflow and template --- Makefile | 14 +++------- docs/_templates/package.rst_t | 51 +++++++++++++++++++++++++++++++++++ docs/conf.py | 6 +++++ docs/cookiecutter.rst | 1 - docs/requirements.txt | 2 +- noxfile.py | 5 +++- 6 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 docs/_templates/package.rst_t diff --git a/Makefile b/Makefile index 2dc6aa3aa..8c993be1e 100644 --- a/Makefile +++ b/Makefile @@ -60,21 +60,13 @@ coverage: ## Check code coverage quickly with the default Python .PHONY: docs docs: ## Generate Sphinx HTML documentation, including API docs @echo "+ $@" - @rm -f docs/cookiecutter.rst - @sphinx-apidoc -o docs/ cookiecutter - @rm -f docs/modules.rst - @sed -i 's/cookiecutter package/===\nAPI\n===/' docs/cookiecutter.rst - @sed -i 's/====================//' docs/cookiecutter.rst - @sed -i 's/Submodules/This is the Cookiecutter modules API documentation./' docs/cookiecutter.rst - @sed -i 's/^----------$$//' docs/cookiecutter.rst - @$(MAKE) -C docs clean - @$(MAKE) -C docs html + @nox --non-interactive -s docs @$(BROWSER) docs/_build/html/index.html .PHONY: servedocs -servedocs: docs ## Rebuild docs automatically +servedocs: ## Rebuild docs automatically @echo "+ $@" - @watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . + @nox -s docs .PHONY: submodules submodules: ## Pull and update git submodules recursively diff --git a/docs/_templates/package.rst_t b/docs/_templates/package.rst_t new file mode 100644 index 000000000..b18365dec --- /dev/null +++ b/docs/_templates/package.rst_t @@ -0,0 +1,51 @@ +{%- macro automodule(modname, options) -%} +.. automodule:: {{ modname }} +{%- for option in options %} + :{{ option }}: +{%- endfor %} +{%- endmacro %} +{%- macro toctree(docnames) -%} +.. toctree:: + :maxdepth: {{ maxdepth }} +{% for docname in docnames %} + {{ docname }} +{%- endfor %} +{%- endmacro -%} +=== +API +=== + + +{%- if modulefirst and not is_namespace %} +{{ automodule(pkgname, automodule_options) }} +{% endif %} + +{%- if subpackages %} +Subpackages +----------- + +{{ toctree(subpackages) }} +{% endif %} + +{%- if submodules %} + +This is the Cookiecutter modules API documentation. + +{% if separatemodules %} +{{ toctree(submodules) }} +{% else %} +{%- for submodule in submodules %} +{% if show_headings %} +{{- [submodule, "module"] | join(" ") | e | heading(2) }} +{% endif %} +{{ automodule(submodule, automodule_options) }} +{% endfor %} +{%- endif %} +{%- endif %} + +{%- if not modulefirst and not is_namespace %} +Module contents +--------------- + +{{ automodule(pkgname, automodule_options) }} +{% endif %} diff --git a/docs/conf.py b/docs/conf.py index cb4be1464..d608b0308 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -54,6 +54,7 @@ 'sphinx.ext.viewcode', 'sphinx_click.ext', 'myst_parser', + 'sphinxcontrib.apidoc', ] # Add any paths that contain templates here, relative to this directory. @@ -359,3 +360,8 @@ "fieldlist", ] myst_heading_anchors = 3 +# Apidoc extension config +apidoc_module_dir = "../cookiecutter" +apidoc_output_dir = "." +apidoc_toc_file = False +apidoc_extra_args = ["-t", "_templates"] diff --git a/docs/cookiecutter.rst b/docs/cookiecutter.rst index 16c1c9dc1..89c857dc4 100644 --- a/docs/cookiecutter.rst +++ b/docs/cookiecutter.rst @@ -2,7 +2,6 @@ API === - This is the Cookiecutter modules API documentation. diff --git a/docs/requirements.txt b/docs/requirements.txt index 14510c6a8..89204a752 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ -watchdog>=0.10.2 sphinx-rtd-theme>=1.0.0 sphinx-click>=4.1.0 myst-parser>=0.17.2 sphinx-autobuild>=2021.3.14 Sphinx>=4.0.1 +sphinxcontrib-apidoc>=0.3.0 diff --git a/noxfile.py b/noxfile.py index d2789664b..e1b0e021f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -2,6 +2,9 @@ Nox is Tox tool replacement. """ +import shutil +from pathlib import Path + import nox nox.options.keywords = "not docs" @@ -50,7 +53,7 @@ def documentation_tests(session): @nox.session(python="3.10") def docs(session, batch_run: bool = False): """Build the documentation or serve documentation interactively.""" - session.run("rm", "-rf", "docs/_build", external=True) + shutil.rmtree(Path("docs").joinpath("_build"), ignore_errors=True) session.install("-r", "docs/requirements.txt") session.install(".") session.cd("docs") From 567ab632839576885ec80c4e741bed2e742c60fa Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 09:04:11 +0300 Subject: [PATCH 115/202] Whatch all codebase for changes in docs --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index e1b0e021f..ffe49f360 100644 --- a/noxfile.py +++ b/noxfile.py @@ -69,7 +69,7 @@ def docs(session, batch_run: bool = False): "--port", "9812", "--watch", - "../cookiecutter", + "../", ] ) From 8c9e1e532b1fd5bd551c7de0bf3eebbbd25e07e2 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 09:11:41 +0300 Subject: [PATCH 116/202] Extend makefile with new clean options --- Makefile | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8c993be1e..a39716fde 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,28 @@ clean-tox: ## Remove tox testing artifacts @echo "+ $@" @rm -rf .tox/ +.PHONY: clean-nox +clean-nox: ## Remove nox testing artifacts + @echo "+ $@" + @rm -rf .nox/ + +.PHONY: clean-coverage +clean-coverage: ## Remove coverage reports + @echo "+ $@" + @rm -rf htmlcov/ + @rm -rf .coverage + @rm -rf .coverage.xml + +.PHONY: clean-pytest +clean-pytest: ## Remove pytest cache + @echo "+ $@" + @rm -rf .pytest_cache/ + +.PHONY: clean-docs-build +clean-docs-build: ## Remove local docs + @echo "+ $@" + @rm -rf docs/_build + .PHONY: clean-build clean-build: ## Remove build artifacts @echo "+ $@" @@ -33,8 +55,8 @@ clean-pyc: ## Remove Python file artifacts @find . -type f -name '*.py[co]' -exec rm -f {} + @find . -name '*~' -exec rm -f {} + -.PHONY: clean -clean: clean-tox clean-build clean-pyc ## Remove all file artifacts +.PHONY: clean ## Remove all file artifacts +clean: clean-tox clean-build clean-pyc clean-nox clean-coverage clean-pytest clean-docs-build .PHONY: lint lint: ## Check code style From 60c36ffd7bb106d8a25e75c1afd9106082fb2dac Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 09:14:47 +0300 Subject: [PATCH 117/202] Replace lint in makefile --- Makefile | 2 +- noxfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a39716fde..a2701eac6 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ clean: clean-tox clean-build clean-pyc clean-nox clean-coverage clean-pytest cle .PHONY: lint lint: ## Check code style @echo "+ $@" - @tox -e lint + @nox -s lint .PHONY: test test: ## Run tests quickly with the default Python diff --git a/noxfile.py b/noxfile.py index ffe49f360..aa5de2e2b 100644 --- a/noxfile.py +++ b/noxfile.py @@ -20,7 +20,7 @@ def base_install(session): @nox.session(python="3.10") def lint(session): """Run linting check locally.""" - session = base_install(session) + session.install("pre-commit") session.run("pre-commit", "run", "-a") From 5f71bc6235038607befe1bdd6b714a1e63def6a9 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 09:15:33 +0300 Subject: [PATCH 118/202] Fix typo in filename --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a2701eac6..6a287a97c 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ clean-coverage: ## Remove coverage reports @echo "+ $@" @rm -rf htmlcov/ @rm -rf .coverage - @rm -rf .coverage.xml + @rm -rf coverage.xml .PHONY: clean-pytest clean-pytest: ## Remove pytest cache From b64ea6386a30d5e8571dc13f7417cead008797e8 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 09:30:46 +0300 Subject: [PATCH 119/202] Replace tox make steps with nox --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 6a287a97c..0392aeb33 100644 --- a/Makefile +++ b/Makefile @@ -66,17 +66,17 @@ lint: ## Check code style .PHONY: test test: ## Run tests quickly with the default Python @echo "+ $@" - @tox -e py + @nox -p 3.10 .PHONY: test-all test-all: ## Run tests on every Python version with tox @echo "+ $@" - @tox + @nox .PHONY: coverage coverage: ## Check code coverage quickly with the default Python @echo "+ $@" - @tox -e cov-report + @nox -s tests -p 3.10 @$(BROWSER) htmlcov/index.html .PHONY: docs From f073dd5a6b122fc757d475ae529c15f9e294aff7 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 10:05:49 +0300 Subject: [PATCH 120/202] Update documentation workflow to use nox --- .github/workflows/docs.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d3744b683..4a0299117 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,19 +8,19 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: 'recursive' - - uses: actions/setup-python@v1 + - uses: actions/setup-python@v3 with: - python-version: "3.7" + python-version: "3.10" - name: Install dependencies run: | python -m pip install --upgrade pip - pip install tox virtualenv + pip install nox virtualenv - name: Build docs - run: tox -e docs - - uses: actions/upload-artifact@v1 + run: nox -s docs + - uses: actions/upload-artifact@v3 with: name: DocumentationHTML path: docs/_build/html/ From 292ccdaf72f1d688eac012df0d1db82b7446aaad Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 10:12:06 +0300 Subject: [PATCH 121/202] Update github actions major versions --- .github/workflows/main.yml | 14 +++++++------- .github/workflows/pip-publish.yml | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8fb12c2b4..61d05c901 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,10 +14,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: - python-version: "3.9" + python-version: "3.10" - name: Install dependencies run: | python -m pip install --upgrade pip @@ -100,9 +100,9 @@ jobs: tox_env: "py310" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python }} - name: Install dependencies @@ -112,14 +112,14 @@ jobs: - name: Test build run: "tox -e ${{ matrix.tox_env }}-cov-report" - name: Send coverage report to codeclimate - uses: paambaati/codeclimate-action@v2.6.0 + uses: paambaati/codeclimate-action@v3 with: coverageCommand: echo "Ignore rerun" coverageLocations: ${{github.workspace}}/coverage.xml:coverage.py env: CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}} - name: Send coverage report to codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: env_vars: OS=${{ matrix.os }},PYTHON=${{ matrix.python }} file: ./coverage.xml diff --git a/.github/workflows/pip-publish.yml b/.github/workflows/pip-publish.yml index c5e01d9e1..1c6ad40bf 100644 --- a/.github/workflows/pip-publish.yml +++ b/.github/workflows/pip-publish.yml @@ -14,10 +14,10 @@ jobs: steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v3 with: python-version: 3.9 From 15be977e322b2622c2cd28a8e2c2083b36f0f930 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 10:26:46 +0300 Subject: [PATCH 122/202] Github Actions: Tox with Nox replace + Safety check for all python versions --- .github/workflows/main.yml | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 61d05c901..50cbe9375 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,11 +21,9 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install tox virtualenv + pip install nox virtualenv - name: Lint - run: "tox -e lint" - - name: Safety - run: "tox -e safety" + run: "nox -s lint" build: runs-on: ${{ matrix.os }} strategy: @@ -51,53 +49,41 @@ jobs: - name: "ubuntu-py37" python: "3.7" os: ubuntu-latest - tox_env: "py37" - name: "ubuntu-py38" python: "3.8" os: ubuntu-latest - tox_env: "py38" - name: "ubuntu-py39" python: "3.9" os: ubuntu-latest - tox_env: "py39" - name: "ubuntu-py310" python: "3.10" os: ubuntu-latest - tox_env: "py310" - name: "macos-py37" python: "3.7" os: macos-latest - tox_env: "py37" - name: "macos-py38" python: "3.8" os: macos-latest - tox_env: "py38" - name: "macos-py39" python: "3.9" os: macos-latest - tox_env: "py39" - name: "macos-py310" python: "3.10" os: macos-latest - tox_env: "py310" - name: "windows-py37" python: "3.7" os: windows-latest - tox_env: "py37" - name: "windows-py38" python: "3.8" os: windows-latest - tox_env: "py38" - name: "windows-py39" python: "3.9" os: windows-latest - tox_env: "py39" - name: "windows-py310" python: "3.10" os: windows-latest - tox_env: "py310" steps: - uses: actions/checkout@v3 @@ -108,9 +94,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install tox virtualenv - - name: Test build - run: "tox -e ${{ matrix.tox_env }}-cov-report" + pip install nox virtualenv + - name: Project internals test build + run: "nox -p ${{ matrix.python }} -s tests" + - name: Project security test + run: "nox -p ${{ matrix.python }} -s safety_tests" - name: Send coverage report to codeclimate uses: paambaati/codeclimate-action@v3 with: From 1179fe020b79dbe958d0d0522e3e6af8164ded2f Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 10:29:10 +0300 Subject: [PATCH 123/202] Use python 3.10 for deploy to pip --- .github/workflows/pip-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pip-publish.yml b/.github/workflows/pip-publish.yml index 1c6ad40bf..ae4cb3c7a 100644 --- a/.github/workflows/pip-publish.yml +++ b/.github/workflows/pip-publish.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v3 with: - python-version: 3.9 + python-version: 3.10 - name: Install pypa/build run: >- From d8625b1b98ffa48522cf86a3d3f97d8ff2c6e6d2 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 10:53:53 +0300 Subject: [PATCH 124/202] Fix codeclimate version --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 50cbe9375..5706edb44 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -100,7 +100,7 @@ jobs: - name: Project security test run: "nox -p ${{ matrix.python }} -s safety_tests" - name: Send coverage report to codeclimate - uses: paambaati/codeclimate-action@v3 + uses: paambaati/codeclimate-action@v3.0.0 with: coverageCommand: echo "Ignore rerun" coverageLocations: ${{github.workspace}}/coverage.xml:coverage.py From 86b47cd3d2bdf06d7bcef26cbdabe63580c34e58 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 11:06:42 +0300 Subject: [PATCH 125/202] Simplify github actions matrix definition --- .github/workflows/main.yml | 69 ++++++-------------------------------- 1 file changed, 11 insertions(+), 58 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5706edb44..822e11e35 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ on: - "*" jobs: - lint: + linting: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -24,67 +24,20 @@ jobs: pip install nox virtualenv - name: Lint run: "nox -s lint" - build: + tests_run: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - name: - - "ubuntu-py37" - - "ubuntu-py38" - - "ubuntu-py39" - - "ubuntu-py310" - - - "macos-py37" - - "macos-py38" - - "macos-py39" - - "macos-py310" - - - "windows-py37" - - "windows-py38" - - "windows-py39" - - "windows-py310" - - include: - - name: "ubuntu-py37" - python: "3.7" - os: ubuntu-latest - - name: "ubuntu-py38" - python: "3.8" - os: ubuntu-latest - - name: "ubuntu-py39" - python: "3.9" - os: ubuntu-latest - - name: "ubuntu-py310" - python: "3.10" - os: ubuntu-latest - - - name: "macos-py37" - python: "3.7" - os: macos-latest - - name: "macos-py38" - python: "3.8" - os: macos-latest - - name: "macos-py39" - python: "3.9" - os: macos-latest - - name: "macos-py310" - python: "3.10" - os: macos-latest - - - name: "windows-py37" - python: "3.7" - os: windows-latest - - name: "windows-py38" - python: "3.8" - os: windows-latest - - name: "windows-py39" - python: "3.9" - os: windows-latest - - name: "windows-py310" - python: "3.10" - os: windows-latest - + os: + - ubuntu-latest + - macos-latest + - windows-latest + python: + - "3.7" + - "3.8" + - "3.9" + - "3.10" steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python }} From a94e2ae10831b851f766c7ebe35ce3eba9f9ccfe Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Mon, 6 Jun 2022 23:21:37 +0300 Subject: [PATCH 126/202] Rename release-drafter yml to exclude IDE known formats warning --- .github/workflows/{release-drafter.yml => drafter.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{release-drafter.yml => drafter.yml} (100%) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/drafter.yml similarity index 100% rename from .github/workflows/release-drafter.yml rename to .github/workflows/drafter.yml From 09c19a39ad050a7e1973c4c5da55e60cc3a08b13 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 11:16:37 +0300 Subject: [PATCH 127/202] Rename main workflow to tests workflow --- .github/workflows/{main.yml => tests.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{main.yml => tests.yml} (98%) diff --git a/.github/workflows/main.yml b/.github/workflows/tests.yml similarity index 98% rename from .github/workflows/main.yml rename to .github/workflows/tests.yml index 822e11e35..bd686f993 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,4 @@ -name: CI Tests +name: CI/CD Tests on: push: From b1f8ca648e0516f9589b2e2d6404e57cdcb74f55 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 11:18:29 +0300 Subject: [PATCH 128/202] Move documentation workflow to main tests workflow to see all on one screen --- .github/workflows/docs.yml | 26 -------------------------- .github/workflows/tests.yml | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 26 deletions(-) delete mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 4a0299117..000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: "Documentation build check" -on: - pull_request: - branches: - - "*" - -jobs: - docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: 'recursive' - - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install nox virtualenv - - name: Build docs - run: nox -s docs - - uses: actions/upload-artifact@v3 - with: - name: DocumentationHTML - path: docs/_build/html/ diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bd686f993..f4963bda3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,6 +11,25 @@ on: - "*" jobs: + documentation_build_test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: 'recursive' + - uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install nox virtualenv + - name: Build docs + run: nox -s docs + - uses: actions/upload-artifact@v3 + with: + name: DocumentationHTML + path: docs/_build/html/ linting: runs-on: ubuntu-latest steps: From 8fd1194e3d8a7541035e8e4771354c958faab2c7 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 11:30:20 +0300 Subject: [PATCH 129/202] Attempt to fix design problems on read the docs --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 89204a752..cd980601c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,5 +2,5 @@ sphinx-rtd-theme>=1.0.0 sphinx-click>=4.1.0 myst-parser>=0.17.2 sphinx-autobuild>=2021.3.14 -Sphinx>=4.0.1 +Sphinx>=4.5.0 sphinxcontrib-apidoc>=0.3.0 From a263ad0e01a4eccf9addf3eb40c1a52e24101250 Mon Sep 17 00:00:00 2001 From: Italo Maia Date: Mon, 6 Jun 2022 23:17:35 +0200 Subject: [PATCH 130/202] Update boolean_variables.rst Adding explanation on what is considered valid input for boolean configuration. --- docs/advanced/boolean_variables.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/advanced/boolean_variables.rst b/docs/advanced/boolean_variables.rst index 58c5e48a7..964049152 100644 --- a/docs/advanced/boolean_variables.rst +++ b/docs/advanced/boolean_variables.rst @@ -20,7 +20,10 @@ you'd get the following user input when running Cookiecutter:: run_as_docker [True]: -Depending on the user's input, a different condition will be selected. +The following values are considered valid input: + + true, 1, yes, y **or** + false, 0, no, n The above ``run_as_docker`` boolean variable creates ``cookiecutter.run_as_docker``, which can be used like this:: From c6dff1b9bd0f4b29eeb4cd65bd559a3202d622fd Mon Sep 17 00:00:00 2001 From: Italo Maia Date: Tue, 7 Jun 2022 00:15:20 +0200 Subject: [PATCH 131/202] Update boolean_variables.rst Removing trailing whitespace --- docs/advanced/boolean_variables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/boolean_variables.rst b/docs/advanced/boolean_variables.rst index 964049152..85d4bc461 100644 --- a/docs/advanced/boolean_variables.rst +++ b/docs/advanced/boolean_variables.rst @@ -23,7 +23,7 @@ you'd get the following user input when running Cookiecutter:: The following values are considered valid input: true, 1, yes, y **or** - false, 0, no, n + false, 0, no, n The above ``run_as_docker`` boolean variable creates ``cookiecutter.run_as_docker``, which can be used like this:: From 1ad2375fc4582572f25c5e668fb541d72e33bab6 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 13:47:04 +0300 Subject: [PATCH 132/202] Fix CI/CD for first time contributors --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f4963bda3..a84a59992 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -72,6 +72,7 @@ jobs: - name: Project security test run: "nox -p ${{ matrix.python }} -s safety_tests" - name: Send coverage report to codeclimate + continue-on-error: true uses: paambaati/codeclimate-action@v3.0.0 with: coverageCommand: echo "Ignore rerun" From 6008c7123b0a7dbaf8495b4ceaae840bb478d6fc Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 15:10:27 +0300 Subject: [PATCH 133/202] Update documentation intersphinx_mapping to external sources --- docs/conf.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index d608b0308..332153029 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -353,7 +353,11 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/3': None} +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "requests": ("https://requests.readthedocs.io/en/latest/", None), + "click": ("https://click.palletsprojects.com/en/latest", None), +} myst_enable_extensions = [ "tasklist", "strikethrough", From 4d40ed5ea3e0ff79264861d1ffbedbc5c01e3de5 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 15:11:08 +0300 Subject: [PATCH 134/202] Update list of directories for looking for files changes in interactive documentation build --- noxfile.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index aa5de2e2b..25189967e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -55,7 +55,7 @@ def docs(session, batch_run: bool = False): """Build the documentation or serve documentation interactively.""" shutil.rmtree(Path("docs").joinpath("_build"), ignore_errors=True) session.install("-r", "docs/requirements.txt") - session.install(".") + session.install("-e", ".") session.cd("docs") sphinx_args = ["-b", "html", "-W", ".", "_build/html"] @@ -69,7 +69,13 @@ def docs(session, batch_run: bool = False): "--port", "9812", "--watch", - "../", + "../*.md", + "--watch", + "../*.rst", + "--watch", + "../*.py", + "--watch", + "../cookiecutter", ] ) From fbc68f7456a4e152dc8c6443c1d688fd0d56c955 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 15:12:06 +0300 Subject: [PATCH 135/202] Extend read_user_yes_no docstrings --- cookiecutter/prompt.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index 003ace67d..d27481f7b 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -16,20 +16,23 @@ def read_user_variable(var_name, default_value): :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 https://click.palletsprojects.com/en/7.x/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' + - These input values will be converted to ``True``: + "1", "true", "t", "yes", "y", "on" + - These input values will be converted to ``False``: + "0", "false", "f", "no", "n", "off" + + Actual parsing done by :func:`click.prompt`; Check this function codebase change in + case of unexpected behaviour. :param str question: Question to the user :param default_value: Value that will be returned if no input happens """ - # Please see https://click.palletsprojects.com/en/7.x/api/#click.prompt return click.prompt(question, default=default_value, type=click.BOOL) @@ -38,7 +41,6 @@ def read_repo_password(question): :param str question: Question to the user """ - # Please see https://click.palletsprojects.com/en/7.x/api/#click.prompt return click.prompt(question, hide_input=True) @@ -51,7 +53,6 @@ def read_user_choice(var_name, options): :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 https://click.palletsprojects.com/en/7.x/api/#click.prompt if not isinstance(options, list): raise TypeError @@ -109,7 +110,6 @@ def read_user_dict(var_name, default_value): :param default_value: Value that will be returned if no input is provided :return: A Python dictionary to use in the context. """ - # Please see https://click.palletsprojects.com/en/7.x/api/#click.prompt if not isinstance(default_value, dict): raise TypeError From 621739292da406b549f44a09aca76b5b4c46671f Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Tue, 7 Jun 2022 16:14:53 +0300 Subject: [PATCH 136/202] Finalize Boolean Variables documentation update (rst) --- docs/advanced/boolean_variables.rst | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/advanced/boolean_variables.rst b/docs/advanced/boolean_variables.rst index 85d4bc461..8d9cf6997 100644 --- a/docs/advanced/boolean_variables.rst +++ b/docs/advanced/boolean_variables.rst @@ -1,32 +1,35 @@ -.. _boolean-variables: +Boolean Variables +----------------- -Boolean Variables (2.2+) ------------------------- +.. versionadded:: 2.2.0 Boolean variables are used for answering True/False questions. Basic Usage ~~~~~~~~~~~ -Boolean variables are regular key / value pairs, but with the value being True/False. +Boolean variables are regular key / value pairs, but with the value being +``True``/``False``. -For example, if you provide the following boolean variable in your ``cookiecutter.json``:: +For example, if you provide the following boolean variable in your +``cookiecutter.json``:: { "run_as_docker": true } -you'd get the following user input when running Cookiecutter:: +you will get the following user input when running Cookiecutter:: run_as_docker [True]: -The following values are considered valid input: +User input will be parsed by :func:`~cookiecutter.prompt.read_user_yes_no`. The +following values are considered as valid user input: - true, 1, yes, y **or** - false, 0, no, n + - ``True`` values: "1", "true", "t", "yes", "y", "on" + - ``False`` values: "0", "false", "f", "no", "n", "off" -The above ``run_as_docker`` boolean variable creates ``cookiecutter.run_as_docker``, which -can be used like this:: +The above ``run_as_docker`` boolean variable creates ``cookiecutter.run_as_docker``, +which can be used like this:: {%- if cookiecutter.run_as_docker -%} # In case of True add your content here @@ -36,7 +39,8 @@ can be used like this:: {% endif %} -Cookiecutter is using `Jinja2's if conditional expression `_ to determine the correct ``run_as_docker``. +Cookiecutter is using `Jinja2's if conditional expression `_ to determine the correct ``run_as_docker``. Input Validation ~~~~~~~~~~~~~~~~ From 252478aa03330aa7408f85b6053ab7572ca364a9 Mon Sep 17 00:00:00 2001 From: Johann Christensen Date: Tue, 7 Jun 2022 13:42:32 +0200 Subject: [PATCH 137/202] Fix Python code block Due to the `::` after the `Or use the Python API`, the following code block was rendered incorrectly. --- docs/advanced/replay.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/replay.rst b/docs/advanced/replay.rst index 426f0274e..79672e284 100644 --- a/docs/advanced/replay.rst +++ b/docs/advanced/replay.rst @@ -37,7 +37,7 @@ Pass the according option on the CLI: cookiecutter --replay gh:hackebrot/cookiedozer -Or use the Python API:: +Or use the Python API: .. code-block:: python From 17c07bf9fb7369c4030a42feeba70c72ce38a9bb Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 13:36:49 +0300 Subject: [PATCH 138/202] Replace strings methods with f-string (tests) --- tests/test_custom_extensions_in_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_custom_extensions_in_hooks.py b/tests/test_custom_extensions_in_hooks.py index cd5f8aa3a..4300bbf4d 100644 --- a/tests/test_custom_extensions_in_hooks.py +++ b/tests/test_custom_extensions_in_hooks.py @@ -18,7 +18,7 @@ ) def template(request): """Fixture. Allows to split pre and post hooks test directories.""" - return 'tests/test-extensions/' + request.param + return f"tests/test-extensions/{request.param}" @pytest.fixture(autouse=True) From 6d92aae82537b85c04eac22eb3fc601fcd258f65 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 13:37:47 +0300 Subject: [PATCH 139/202] Simplify 'if' block in utils tests --- tests/test_utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 988fc3887..537f1017f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -175,10 +175,7 @@ def test_prompt_should_ask_and_keep_repo_on_reuse(mocker, tmp_path): cloned template repo, it should not be deleted.""" def answer(question, default): - if 'okay to delete' in question: - return False - else: - return True + return 'okay to delete' not in question mock_read_user = mocker.patch( 'cookiecutter.utils.read_user_yes_no', side_effect=answer, autospec=True From efd5dce699dd5b48ba681d8724c67fe9ef3c2301 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 13:38:23 +0300 Subject: [PATCH 140/202] Remove meaningless test statement (test_jinja2_uuid_extension) --- tests/test_default_extensions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_default_extensions.py b/tests/test_default_extensions.py index 540138d31..85924a68e 100644 --- a/tests/test_default_extensions.py +++ b/tests/test_default_extensions.py @@ -61,4 +61,3 @@ def test_jinja2_uuid_extension(tmp_path): changelog_lines = f.readlines() uuid.UUID(changelog_lines[0], version=4) - assert True From 1d1869636f97654800581e07e0b28dbf1e4c3ec2 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:45:48 +0300 Subject: [PATCH 141/202] Tests: Use pathlib for files read/write (test_cli.py) --- tests/test_cli.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 1bc2fdd55..45a4a0019 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3,7 +3,7 @@ import json import os import re - +from pathlib import Path import pytest from click.testing import CliRunner @@ -72,7 +72,7 @@ def test_cli(cli_runner): result = cli_runner('tests/fake-repo-pre/', '--no-input') assert result.exit_code == 0 assert os.path.isdir('fake-project') - with open(os.path.join('fake-project', 'README.rst')) as f: + with Path("fake-project", "README.rst").open() as f: assert 'Project name: **Fake Project**' in f.read() @@ -82,7 +82,7 @@ def test_cli_verbose(cli_runner): result = cli_runner('tests/fake-repo-pre/', '--no-input', '-v') assert result.exit_code == 0 assert os.path.isdir('fake-project') - with open(os.path.join('fake-project', 'README.rst')) as f: + with Path("fake-project", "README.rst").open() as f: assert 'Project name: **Fake Project**' in f.read() @@ -435,7 +435,7 @@ def test_local_extension(tmpdir, cli_runner): template_path, ) assert result.exit_code == 0 - with open(os.path.join(output_dir, 'Foobar', 'HISTORY.rst')) as f: + with Path(output_dir, 'Foobar', 'HISTORY.rst').open() as f: data = f.read() assert 'FoobarFoobar' in data assert 'FOOBAR' in data @@ -462,7 +462,7 @@ def test_cli_extra_context(cli_runner): ) assert result.exit_code == 0 assert os.path.isdir('fake-project') - with open(os.path.join('fake-project', 'README.rst')) as f: + with Path('fake-project', 'README.rst').open() as f: assert 'Project name: **Awesomez**' in f.read() @@ -544,13 +544,14 @@ def test_debug_list_installed_templates(cli_runner, debug_file, user_config_path """Verify --list-installed command correct invocation.""" fake_template_dir = os.path.dirname(os.path.abspath('fake-project')) os.makedirs(os.path.dirname(user_config_path)) - with open(user_config_path, 'w') as config_file: + with Path(user_config_path).open('w') as config_file: # In YAML, double quotes mean to use escape sequences. # Single quotes mean we will have unescaped backslahes. # http://blogs.perl.org/users/tinita/2018/03/ # strings-in-yaml---to-quote-or-not-to-quote.html config_file.write("cookiecutters_dir: '%s'" % fake_template_dir) - open(os.path.join('fake-project', 'cookiecutter.json'), 'w').write('{}') + with Path("fake-project", "cookiecutter.json").open("w") as f: + f.write('{}') result = cli_runner( '--list-installed', @@ -568,7 +569,7 @@ def test_debug_list_installed_templates_failure( ): """Verify --list-installed command error on invocation.""" os.makedirs(os.path.dirname(user_config_path)) - with open(user_config_path, 'w') as config_file: + with Path(user_config_path).open('w') as config_file: config_file.write('cookiecutters_dir: "/notarealplace/"') result = cli_runner( @@ -590,7 +591,7 @@ def test_directory_repo(cli_runner): ) assert result.exit_code == 0 assert os.path.isdir("fake-project") - with open(os.path.join("fake-project", "README.rst")) as f: + with Path("fake-project", "README.rst").open() as f: assert "Project name: **Fake Project**" in f.read() From d233e33e288e3f573c0f1f1206137c1344c562ef Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:46:03 +0300 Subject: [PATCH 142/202] Tests: Use pathlib for files read/write (test_hooks.py) --- tests/test_hooks.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/test_hooks.py b/tests/test_hooks.py index d214e714b..ce4a2f426 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -1,9 +1,10 @@ """Tests for `cookiecutter.hooks` module.""" -import os import errno +import os import stat import sys import textwrap +from pathlib import Path import pytest @@ -18,10 +19,10 @@ def make_test_repo(name, multiple_hooks=False): os.mkdir(hook_dir) os.mkdir(template) - with open(os.path.join(template, 'README.rst'), 'w') as f: + with Path(template, 'README.rst').open('w') as f: f.write("foo\n===\n\nbar\n") - with open(os.path.join(hook_dir, 'pre_gen_project.py'), 'w') as f: + with Path(hook_dir, 'pre_gen_project.py').open('w') as f: f.write("#!/usr/bin/env python\n") f.write("# -*- coding: utf-8 -*-\n") f.write("from __future__ import print_function\n") @@ -32,7 +33,7 @@ def make_test_repo(name, multiple_hooks=False): if sys.platform.startswith('win'): post = 'post_gen_project.bat' - with open(os.path.join(hook_dir, post), 'w') as f: + with Path(hook_dir, post).open('w') as f: f.write("@echo off\n") f.write("\n") f.write("echo post generation hook\n") @@ -40,7 +41,7 @@ def make_test_repo(name, multiple_hooks=False): else: post = 'post_gen_project.sh' filename = os.path.join(hook_dir, post) - with open(filename, 'w') as f: + with Path(filename).open('w') as f: f.write("#!/bin/bash\n") f.write("\n") f.write("echo 'post generation hook';\n") @@ -52,7 +53,7 @@ def make_test_repo(name, multiple_hooks=False): if multiple_hooks: if sys.platform.startswith('win'): pre = 'pre_gen_project.bat' - with open(os.path.join(hook_dir, pre), 'w') as f: + with Path(hook_dir, pre).open('w') as f: f.write("@echo off\n") f.write("\n") f.write("echo post generation hook\n") @@ -60,7 +61,7 @@ def make_test_repo(name, multiple_hooks=False): else: pre = 'pre_gen_project.sh' filename = os.path.join(hook_dir, pre) - with open(filename, 'w') as f: + with Path(filename).open('w') as f: f.write("#!/bin/bash\n") f.write("\n") f.write("echo 'post generation hook';\n") @@ -182,13 +183,13 @@ def test_run_script_with_context(self): if sys.platform.startswith('win'): post = 'post_gen_project.bat' - with open(os.path.join(self.hooks_path, post), 'w') as f: + with Path(self.hooks_path, post).open('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: + with Path(hook_path).open('w') as fh: fh.write("#!/bin/bash\n") fh.write("\n") fh.write("echo 'post generation hook';\n") @@ -221,7 +222,7 @@ 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: + with Path(hook_path).open('w') as f: f.write("#!/usr/bin/env python\n") f.write("import sys; sys.exit(1)\n") From 6840956054bedf9dad5f219ea4c69717d811ee3c Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:46:19 +0300 Subject: [PATCH 143/202] Tests: Use pathlib for files read/write (test_utils.py) --- tests/test_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 988fc3887..d144d7eea 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -18,7 +18,7 @@ def test_force_delete(mocker, tmp_path): """Verify `utils.force_delete` makes files writable.""" ro_file = Path(tmp_path, 'bar') - with open(ro_file, "w") as f: + with Path(ro_file).open("w") as f: f.write("Test data") make_readonly(ro_file) @@ -33,7 +33,7 @@ def test_force_delete(mocker, tmp_path): def test_rmtree(tmp_path): """Verify `utils.rmtree` remove files marked as read-only.""" - with open(Path(tmp_path, 'bar'), "w") as f: + with Path(tmp_path, "bar").open("w") as f: f.write("Test data") make_readonly(Path(tmp_path, 'bar')) From 71c435fab8ef661811fac2f6fbca0589c357ee0e Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:46:34 +0300 Subject: [PATCH 144/202] Tests: Use pathlib for files read/write (test_unzip.py) --- tests/zipfile/test_unzip.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/zipfile/test_unzip.py b/tests/zipfile/test_unzip.py index 2bf58b00f..9d4448e59 100644 --- a/tests/zipfile/test_unzip.py +++ b/tests/zipfile/test_unzip.py @@ -1,8 +1,9 @@ """Tests for function unzip() from zipfile module.""" +import shutil import tempfile +from pathlib import Path import pytest -import shutil from cookiecutter import zipfile from cookiecutter.exceptions import InvalidZipRepository @@ -10,7 +11,7 @@ def mock_download(): """Fake download function.""" - with open('tests/files/fake-repo-tmpl.zip', 'rb') as zf: + with Path('tests/files/fake-repo-tmpl.zip').open('rb') as zf: chunk = zf.read(1024) while chunk: yield chunk @@ -20,7 +21,7 @@ def mock_download(): def mock_download_with_empty_chunks(): """Fake download function.""" yield - with open('tests/files/fake-repo-tmpl.zip', 'rb') as zf: + with Path('tests/files/fake-repo-tmpl.zip').open('rb') as zf: chunk = zf.read(1024) while chunk: yield chunk From a06d852fed6bac454231c65fbbf79811aca01a06 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:46:50 +0300 Subject: [PATCH 145/202] Tests: Use pathlib for files read/write (test_templates.py) --- tests/test_templates.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_templates.py b/tests/test_templates.py index 27f40428b..ac0e0e85d 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -4,8 +4,7 @@ Tests to ensure custom cookiecutter extensions are properly made available to pre- and post-gen hooks. """ -import codecs -import os +from pathlib import Path import pytest @@ -31,9 +30,7 @@ def test_build_templates(template, output_dir): output_dir=output_dir, ) - readme_file = os.path.join(project_dir, 'requirements.txt') - - with codecs.open(readme_file, encoding='utf8') as f: + with Path(project_dir, 'requirements.txt').open() as f: readme = f.read().splitlines() assert readme == [ From f98f0676f1c1ace76f185c8f7890b4c30a0e6cdb Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:47:14 +0300 Subject: [PATCH 146/202] Tests: Use pathlib for files read/write (test_generate_file.py) --- tests/test_generate_file.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/test_generate_file.py b/tests/test_generate_file.py index 18c811eea..6e6604bea 100644 --- a/tests/test_generate_file.py +++ b/tests/test_generate_file.py @@ -2,6 +2,7 @@ import json import os import re +from pathlib import Path import pytest from jinja2 import FileSystemLoader @@ -45,7 +46,7 @@ def test_generate_file(env): env=env, ) assert os.path.isfile('tests/files/cheese.txt') - with open('tests/files/cheese.txt') as f: + with Path('tests/files/cheese.txt').open() as f: generated_text = f.read() assert generated_text == 'Testing cheese' @@ -58,7 +59,7 @@ def test_generate_file_jsonify_filter(env): project_dir=".", infile=infile, context={'cookiecutter': data}, env=env ) assert os.path.isfile('tests/files/cheese.txt') - with open('tests/files/cheese.txt') as f: + with Path('tests/files/cheese.txt').open() as f: generated_text = f.read() assert json.loads(generated_text) == data @@ -72,7 +73,7 @@ def test_generate_file_random_ascii_string(env, length, punctuation): context = {"cookiecutter": data, "length": length, "punctuation": punctuation} generate.generate_file(project_dir=".", infile=infile, context=context, env=env) assert os.path.isfile('tests/files/cheese.txt') - with open('tests/files/cheese.txt') as f: + with Path('tests/files/cheese.txt').open() as f: generated_text = f.read() assert len(generated_text) == length @@ -92,7 +93,7 @@ def test_generate_file_with_true_condition(env): env=env, ) assert os.path.isfile('tests/files/cheese.txt') - with open('tests/files/cheese.txt') as f: + with Path('tests/files/cheese.txt').open() as f: generated_text = f.read() assert generated_text == 'Testing that generate_file was y' @@ -148,7 +149,7 @@ def test_generate_file_does_not_translate_lf_newlines_to_crlf(env, tmp_path): # this generated file should have a LF line ending gf = 'tests/files/cheese_lf_newlines.txt' - with open(gf, encoding='utf-8', newline='') as f: + with Path(gf).open(newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\n' assert f.newlines == '\n' @@ -166,7 +167,7 @@ def test_generate_file_does_not_translate_crlf_newlines_to_lf(env): # this generated file should have a CRLF line ending gf = 'tests/files/cheese_crlf_newlines.txt' - with open(gf, encoding='utf-8', newline='') as f: + with Path(gf).open(newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\r\n' assert f.newlines == '\r\n' From 8876b10b2673a2ed61b2941066d012e624d85286 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:47:25 +0300 Subject: [PATCH 147/202] Tests: Use pathlib for files read/write (test_generate_files.py) --- tests/test_generate_files.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test_generate_files.py b/tests/test_generate_files.py index 9cf4929aa..436d83ac6 100644 --- a/tests/test_generate_files.py +++ b/tests/test_generate_files.py @@ -44,7 +44,7 @@ def test_generate_files(tmp_path): assert simple_file.exists() assert simple_file.is_file() - simple_text = open(simple_file, encoding='utf-8').read() + simple_text = Path(simple_file).open().read() assert simple_text == 'I eat pizzä' @@ -60,7 +60,7 @@ def test_generate_files_with_linux_newline(tmp_path): assert newline_file.is_file() assert newline_file.exists() - with open(newline_file, encoding='utf-8', newline='') as f: + with Path(newline_file).open(newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\n' assert f.newlines == '\n' @@ -83,7 +83,7 @@ def test_generate_files_with_jinja2_environment(tmp_path): assert conditions_file.is_file() assert conditions_file.exists() - simple_text = conditions_file.open('rt', encoding='utf-8').read() + simple_text = conditions_file.open('rt').read() assert simple_text == 'I eat pizzä\n' @@ -100,7 +100,7 @@ def test_generate_files_with_trailing_newline_forced_to_linux_by_context(tmp_pat assert newline_file.is_file() assert newline_file.exists() - with open(newline_file, encoding='utf-8', newline='') as f: + with Path(newline_file).open(newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\r\n' assert f.newlines == '\r\n' @@ -118,7 +118,7 @@ def test_generate_files_with_windows_newline(tmp_path): assert newline_file.is_file() assert newline_file.exists() - with open(newline_file, encoding='utf-8', newline='') as f: + with Path(newline_file).open(newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\r\n' assert f.newlines == '\r\n' @@ -136,7 +136,7 @@ def test_generate_files_with_windows_newline_forced_to_linux_by_context(tmp_path assert newline_file.is_file() assert newline_file.exists() - with open(newline_file, encoding='utf-8', newline='') as f: + with Path(newline_file).open(newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\n' @@ -241,7 +241,7 @@ def test_generate_files_with_overwrite_if_exists_with_skip_if_file_exists(tmp_pa simple_with_new_line_file = Path(tmp_path, 'inputpizzä/simple-with-newline.txt') Path(tmp_path, 'inputpizzä').mkdir(parents=True) - with open(simple_file, 'w') as f: + with Path(simple_file).open('w') as f: f.write('temp') generate.generate_files( @@ -257,7 +257,7 @@ def test_generate_files_with_overwrite_if_exists_with_skip_if_file_exists(tmp_pa assert Path(simple_with_new_line_file).is_file() assert Path(simple_with_new_line_file).exists() - simple_text = open(simple_file, encoding='utf-8').read() + simple_text = Path(simple_file).open().read() assert simple_text == 'temp' @@ -267,7 +267,7 @@ def test_generate_files_with_skip_if_file_exists(tmp_path): simple_with_new_line_file = Path(tmp_path, 'inputpizzä/simple-with-newline.txt') Path(tmp_path, 'inputpizzä').mkdir(parents=True) - with open(simple_file, 'w') as f: + with Path(simple_file).open('w') as f: f.write('temp') with pytest.raises(exceptions.OutputDirExistsException): @@ -283,7 +283,7 @@ def test_generate_files_with_skip_if_file_exists(tmp_path): assert not Path(simple_with_new_line_file).is_file() assert not Path(simple_with_new_line_file).exists() - simple_text = open(simple_file, encoding='utf-8').read() + simple_text = Path(simple_file).open().read() assert simple_text == 'temp' @@ -293,7 +293,7 @@ def test_generate_files_with_overwrite_if_exists(tmp_path): simple_with_new_line_file = Path(tmp_path, 'inputpizzä/simple-with-newline.txt') Path(tmp_path, 'inputpizzä').mkdir(parents=True) - with open(simple_file, 'w') as f: + with Path(simple_file).open('w') as f: f.write('temp') generate.generate_files( @@ -308,7 +308,7 @@ def test_generate_files_with_overwrite_if_exists(tmp_path): assert Path(simple_with_new_line_file).is_file() assert Path(simple_with_new_line_file).exists() - simple_text = open(simple_file, encoding='utf-8').read() + simple_text = Path(simple_file).open().read() assert simple_text == 'I eat pizzä' From eb7930ee59d4488d2b871039a6a20aae938e832c Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:47:44 +0300 Subject: [PATCH 148/202] Tests: Use pathlib for files read/write (test_generate_hooks.py) --- tests/test_generate_hooks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_generate_hooks.py b/tests/test_generate_hooks.py index 9624bb8ae..a57e0dbde 100644 --- a/tests/test_generate_hooks.py +++ b/tests/test_generate_hooks.py @@ -2,6 +2,7 @@ import errno import os import sys +from pathlib import Path import pytest @@ -123,7 +124,7 @@ def test_run_failing_hook_removes_output_directory(): hook_path = os.path.join(hooks_path, 'pre_gen_project.py') - with open(hook_path, 'w') as f: + with Path(hook_path).open('w') as f: f.write("#!/usr/bin/env python\n") f.write("import sys; sys.exit(1)\n") @@ -152,7 +153,7 @@ def test_run_failing_hook_preserves_existing_output_directory(): hook_path = os.path.join(hooks_path, 'pre_gen_project.py') - with open(hook_path, 'w') as f: + with Path(hook_path).open('w') as f: f.write("#!/usr/bin/env python\n") f.write("import sys; sys.exit(1)\n") From 8e6a475cd15554a4ee54125ef854f34dfc27cdd6 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:48:18 +0300 Subject: [PATCH 149/202] Tests: Use pathlib for files read/write (test_output_folder.py) --- tests/test_output_folder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_output_folder.py b/tests/test_output_folder.py index 166a45005..daea7360c 100644 --- a/tests/test_output_folder.py +++ b/tests/test_output_folder.py @@ -5,6 +5,7 @@ TestOutputFolder.test_output_folder """ import os +from pathlib import Path import pytest @@ -32,11 +33,11 @@ def test_output_folder(): something = """Hi! My name is Audrey Greenfeld. It is 2014.""" - something2 = open('output_folder/something.txt').read() + something2 = Path('output_folder/something.txt').open().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() + in_folder2 = Path('output_folder/folder/in_folder.txt').open().read() assert in_folder == in_folder2 assert os.path.isdir('output_folder/im_a.dir') From eef9a82c56d7248f74c0892912184a670cb3c839 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:48:44 +0300 Subject: [PATCH 150/202] Tests: Use pathlib for files read/write (test_default_extensions.py) --- tests/test_default_extensions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_default_extensions.py b/tests/test_default_extensions.py index 540138d31..13690097f 100644 --- a/tests/test_default_extensions.py +++ b/tests/test_default_extensions.py @@ -1,9 +1,10 @@ """Verify Jinja2 filters/extensions are available from pre-gen/post-gen hooks.""" import os +import uuid +from pathlib import Path import freezegun import pytest -import uuid from cookiecutter.main import cookiecutter @@ -25,7 +26,7 @@ def test_jinja2_time_extension(tmp_path): changelog_file = os.path.join(project_dir, 'HISTORY.rst') assert os.path.isfile(changelog_file) - with open(changelog_file, encoding='utf-8') as f: + with Path(changelog_file).open() as f: changelog_lines = f.readlines() expected_lines = [ @@ -57,7 +58,7 @@ def test_jinja2_uuid_extension(tmp_path): changelog_file = os.path.join(project_dir, 'id') assert os.path.isfile(changelog_file) - with open(changelog_file, encoding='utf-8') as f: + with Path(changelog_file).open() as f: changelog_lines = f.readlines() uuid.UUID(changelog_lines[0], version=4) From 15e3bd05c8d74adcef8db95ae58d80aaff8720cc Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:51:12 +0300 Subject: [PATCH 151/202] Tests: Use pathlib for files read/write (test_cookiecutter_local_no_input.py) --- tests/test_cookiecutter_local_no_input.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_cookiecutter_local_no_input.py b/tests/test_cookiecutter_local_no_input.py index 0031cbcbf..eb1e52ed3 100644 --- a/tests/test_cookiecutter_local_no_input.py +++ b/tests/test_cookiecutter_local_no_input.py @@ -5,6 +5,7 @@ """ import os import textwrap +from pathlib import Path import pytest @@ -65,7 +66,7 @@ def test_cookiecutter_no_input_return_rendered_file(): """Verify Jinja2 templating correctly works in `cookiecutter.json` file.""" project_dir = main.cookiecutter('tests/fake-repo-pre', no_input=True) assert project_dir == os.path.abspath('fake-project') - with open(os.path.join(project_dir, 'README.rst')) as fh: + with Path(project_dir, 'README.rst').open() as fh: contents = fh.read() assert "Project name: **Fake Project**" in contents @@ -76,7 +77,7 @@ def test_cookiecutter_dict_values_in_context(): project_dir = main.cookiecutter('tests/fake-repo-dict', no_input=True) assert project_dir == os.path.abspath('fake-project-dict') - with open(os.path.join(project_dir, 'README.md')) as fh: + with Path(project_dir, 'README.md').open() as fh: contents = fh.read() assert ( From 2477c7956813be73c883e4bbd520c5d5323c7cf5 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:52:22 +0300 Subject: [PATCH 152/202] Tests: Use pathlib for files read/write (test_custom_extensions_in_hooks.py) --- tests/test_custom_extensions_in_hooks.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_custom_extensions_in_hooks.py b/tests/test_custom_extensions_in_hooks.py index cd5f8aa3a..ef9103021 100644 --- a/tests/test_custom_extensions_in_hooks.py +++ b/tests/test_custom_extensions_in_hooks.py @@ -4,8 +4,7 @@ Tests to ensure custom cookiecutter extensions are properly made available to pre- and post-gen hooks. """ -import codecs -import os +from pathlib import Path import pytest @@ -40,9 +39,7 @@ def test_hook_with_extension(template, output_dir): extra_context={'project_slug': 'foobar', 'name': 'Cookiemonster'}, ) - readme_file = os.path.join(project_dir, 'README.rst') - - with codecs.open(readme_file, encoding='utf8') as f: + with Path(project_dir, 'README.rst').open() as f: readme = f.read().strip() assert readme == 'Hello Cookiemonster!' From b40fb40b578fdd5a25bd166f2eb60631de2cdf10 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:53:20 +0300 Subject: [PATCH 153/202] Tests: Use pathlib for files read/write (test_generate_copy_without_render.py) --- tests/test_generate_copy_without_render.py | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/test_generate_copy_without_render.py b/tests/test_generate_copy_without_render.py index 7d614824c..5e4df7b3b 100644 --- a/tests/test_generate_copy_without_render.py +++ b/tests/test_generate_copy_without_render.py @@ -1,5 +1,6 @@ """Verify correct work of `_copy_without_render` context option.""" import os +from pathlib import Path import pytest @@ -43,33 +44,33 @@ def test_generate_copy_without_render_extensions(): assert 'test_copy_without_render-not-rendered' in dir_contents assert 'test_copy_without_render-rendered' in dir_contents - with open('test_copy_without_render/README.txt') as f: + with Path('test_copy_without_render/README.txt').open() as f: assert '{{cookiecutter.render_test}}' in f.read() - with open('test_copy_without_render/README.rst') as f: + with Path('test_copy_without_render/README.rst').open() as f: assert 'I have been rendered!' in f.read() - with open( + with Path( 'test_copy_without_render/test_copy_without_render-rendered/README.txt' - ) as f: + ).open() as f: assert '{{cookiecutter.render_test}}' in f.read() - with open( + with Path( 'test_copy_without_render/test_copy_without_render-rendered/README.rst' - ) as f: + ).open() as f: assert 'I have been rendered' in f.read() - with open( + with Path( 'test_copy_without_render/' 'test_copy_without_render-not-rendered/' 'README.rst' - ) as f: + ).open() as f: assert '{{cookiecutter.render_test}}' in f.read() - with open('test_copy_without_render/rendered/not_rendered.yml') as f: + with Path('test_copy_without_render/rendered/not_rendered.yml').open() as f: assert '{{cookiecutter.render_test}}' in f.read() - with open( + with Path( 'test_copy_without_render/' 'test_copy_without_render-rendered/' 'README.md' - ) as f: + ).open() as f: assert '{{cookiecutter.render_test}}' in f.read() From 14433e1ec2b08ddcd3bcd9044904e66551787808 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:53:33 +0300 Subject: [PATCH 154/202] Tests: Use pathlib for files read/write (test_generate_copy_without_render_override.py) --- ...t_generate_copy_without_render_override.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/test_generate_copy_without_render_override.py b/tests/test_generate_copy_without_render_override.py index af85c4b04..1bda073d9 100644 --- a/tests/test_generate_copy_without_render_override.py +++ b/tests/test_generate_copy_without_render_override.py @@ -1,5 +1,6 @@ """Verify correct work of `_copy_without_render` context option.""" import os +from pathlib import Path import pytest @@ -62,33 +63,33 @@ def test_generate_copy_without_render_extensions(): assert 'test_copy_without_render-not-rendered' in dir_contents assert 'test_copy_without_render-rendered' in dir_contents - with open('test_copy_without_render/README.txt') as f: + with Path('test_copy_without_render/README.txt').open() as f: assert '{{cookiecutter.render_test}}' in f.read() - with open('test_copy_without_render/README.rst') as f: + with Path('test_copy_without_render/README.rst').open() as f: assert 'I have been rendered!' in f.read() - with open( + with Path( 'test_copy_without_render/test_copy_without_render-rendered/README.txt' - ) as f: + ).open() as f: assert '{{cookiecutter.render_test}}' in f.read() - with open( + with Path( 'test_copy_without_render/test_copy_without_render-rendered/README.rst' - ) as f: + ).open() as f: assert 'I have been rendered' in f.read() - with open( + with Path( 'test_copy_without_render/' 'test_copy_without_render-not-rendered/' 'README.rst' - ) as f: + ).open() as f: assert '{{cookiecutter.render_test}}' in f.read() - with open('test_copy_without_render/rendered/not_rendered.yml') as f: + with Path('test_copy_without_render/rendered/not_rendered.yml').open() as f: assert '{{cookiecutter.render_test}}' in f.read() - with open( + with Path( 'test_copy_without_render/' 'test_copy_without_render-rendered/' 'README.md' - ) as f: + ).open() as f: assert '{{cookiecutter.render_test}}' in f.read() From 4aac87e1578bdb39875f38f0f2083077db40d331 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:55:52 +0300 Subject: [PATCH 155/202] Tests: Use pathlib for files read/write (test_determine_repo_dir_finds_existing_cookiecutter.py) --- .../test_determine_repo_dir_finds_existing_cookiecutter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/repository/test_determine_repo_dir_finds_existing_cookiecutter.py b/tests/repository/test_determine_repo_dir_finds_existing_cookiecutter.py index 107e64aa3..ebd7df320 100644 --- a/tests/repository/test_determine_repo_dir_finds_existing_cookiecutter.py +++ b/tests/repository/test_determine_repo_dir_finds_existing_cookiecutter.py @@ -1,5 +1,6 @@ """Tests around detection whether cookiecutter templates are cached locally.""" import os +from pathlib import Path import pytest @@ -20,7 +21,7 @@ def cloned_cookiecutter_path(user_config_data, template): cloned_template_path = os.path.join(cookiecutters_dir, template) os.mkdir(cloned_template_path) - open(os.path.join(cloned_template_path, 'cookiecutter.json'), 'w') + Path(cloned_template_path, "cookiecutter.json").open('w') # creates file return cloned_template_path From b7309ac4b9d1b840195dad3e67627528dbc7a11f Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 14:56:32 +0300 Subject: [PATCH 156/202] Tests: Use pathlib for files read/write (test_determine_repo_dir_finds_existing_cookiecutter.py) --- .../repository/test_determine_repo_dir_finds_subdirectories.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/repository/test_determine_repo_dir_finds_subdirectories.py b/tests/repository/test_determine_repo_dir_finds_subdirectories.py index f40e6063d..a1f7a034c 100644 --- a/tests/repository/test_determine_repo_dir_finds_subdirectories.py +++ b/tests/repository/test_determine_repo_dir_finds_subdirectories.py @@ -1,5 +1,6 @@ """Tests around locally cached cookiecutter template repositories.""" import os +from pathlib import Path import pytest @@ -24,7 +25,7 @@ def cloned_cookiecutter_path(user_config_data, template): subdir_template_path = os.path.join(cloned_template_path, 'my-dir') if not os.path.exists(subdir_template_path): os.mkdir(subdir_template_path) - open(os.path.join(subdir_template_path, 'cookiecutter.json'), 'w') + Path(subdir_template_path, 'cookiecutter.json').open('w') # creates file return subdir_template_path From fe0ce13c5512b8c5ab7f816d17e1666e1d5487f4 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:23:31 +0300 Subject: [PATCH 157/202] Tests: Use pathlib read_text/write_text (test_cli.py) --- tests/test_cli.py | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 45a4a0019..39e924093 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -72,8 +72,8 @@ def test_cli(cli_runner): result = cli_runner('tests/fake-repo-pre/', '--no-input') assert result.exit_code == 0 assert os.path.isdir('fake-project') - with Path("fake-project", "README.rst").open() as f: - assert 'Project name: **Fake Project**' in f.read() + content = Path("fake-project", "README.rst").read_text() + assert 'Project name: **Fake Project**' in content @pytest.mark.usefixtures('remove_fake_project_dir') @@ -82,8 +82,8 @@ def test_cli_verbose(cli_runner): result = cli_runner('tests/fake-repo-pre/', '--no-input', '-v') assert result.exit_code == 0 assert os.path.isdir('fake-project') - with Path("fake-project", "README.rst").open() as f: - assert 'Project name: **Fake Project**' in f.read() + content = Path("fake-project", "README.rst").read_text() + assert 'Project name: **Fake Project**' in content @pytest.mark.usefixtures('remove_fake_project_dir') @@ -435,10 +435,9 @@ def test_local_extension(tmpdir, cli_runner): template_path, ) assert result.exit_code == 0 - with Path(output_dir, 'Foobar', 'HISTORY.rst').open() as f: - data = f.read() - assert 'FoobarFoobar' in data - assert 'FOOBAR' in data + content = Path(output_dir, 'Foobar', 'HISTORY.rst').read_text() + assert 'FoobarFoobar' in content + assert 'FOOBAR' in content def test_local_extension_not_available(tmpdir, cli_runner): @@ -462,8 +461,8 @@ def test_cli_extra_context(cli_runner): ) assert result.exit_code == 0 assert os.path.isdir('fake-project') - with Path('fake-project', 'README.rst').open() as f: - assert 'Project name: **Awesomez**' in f.read() + content = Path('fake-project', 'README.rst').read_text() + assert 'Project name: **Awesomez**' in content @pytest.mark.usefixtures('remove_fake_project_dir') @@ -544,14 +543,12 @@ def test_debug_list_installed_templates(cli_runner, debug_file, user_config_path """Verify --list-installed command correct invocation.""" fake_template_dir = os.path.dirname(os.path.abspath('fake-project')) os.makedirs(os.path.dirname(user_config_path)) - with Path(user_config_path).open('w') as config_file: - # In YAML, double quotes mean to use escape sequences. - # Single quotes mean we will have unescaped backslahes. - # http://blogs.perl.org/users/tinita/2018/03/ - # strings-in-yaml---to-quote-or-not-to-quote.html - config_file.write("cookiecutters_dir: '%s'" % fake_template_dir) - with Path("fake-project", "cookiecutter.json").open("w") as f: - f.write('{}') + # In YAML, double quotes mean to use escape sequences. + # Single quotes mean we will have unescaped backslahes. + # http://blogs.perl.org/users/tinita/2018/03/ + # strings-in-yaml---to-quote-or-not-to-quote.html + Path(user_config_path).write_text(f"cookiecutters_dir: '{fake_template_dir}'") + Path("fake-project", "cookiecutter.json").write_text('{}') result = cli_runner( '--list-installed', @@ -569,8 +566,7 @@ def test_debug_list_installed_templates_failure( ): """Verify --list-installed command error on invocation.""" os.makedirs(os.path.dirname(user_config_path)) - with Path(user_config_path).open('w') as config_file: - config_file.write('cookiecutters_dir: "/notarealplace/"') + Path(user_config_path).write_text('cookiecutters_dir: "/notarealplace/"') result = cli_runner( '--list-installed', '--config-file', user_config_path, str(debug_file) @@ -591,8 +587,8 @@ def test_directory_repo(cli_runner): ) assert result.exit_code == 0 assert os.path.isdir("fake-project") - with Path("fake-project", "README.rst").open() as f: - assert "Project name: **Fake Project**" in f.read() + content = Path("fake-project", "README.rst").read_text() + assert "Project name: **Fake Project**" in content cli_accept_hook_arg_testdata = [ From a3d60399cd4b63dece93af5605f8720c4d0f4692 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:24:01 +0300 Subject: [PATCH 158/202] Tests: Use pathlib read_text/write_text (test_utils.py) --- tests/test_utils.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index d144d7eea..c9c37939d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -17,9 +17,7 @@ def make_readonly(path): def test_force_delete(mocker, tmp_path): """Verify `utils.force_delete` makes files writable.""" ro_file = Path(tmp_path, 'bar') - - with Path(ro_file).open("w") as f: - f.write("Test data") + ro_file.write_text("Test data") make_readonly(ro_file) rmtree = mocker.Mock() @@ -33,9 +31,9 @@ def test_force_delete(mocker, tmp_path): def test_rmtree(tmp_path): """Verify `utils.rmtree` remove files marked as read-only.""" - with Path(tmp_path, "bar").open("w") as f: - f.write("Test data") - make_readonly(Path(tmp_path, 'bar')) + file_path = Path(tmp_path, "bar") + file_path.write_text("Test data") + make_readonly(file_path) utils.rmtree(tmp_path) From 9185db1aca3cb8cc0fe6c34ef6f6f633ffce9d23 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:25:36 +0300 Subject: [PATCH 159/202] Tests: Use pathlib read_text/write_text with encoding (test_templates.py) --- tests/test_templates.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_templates.py b/tests/test_templates.py index ac0e0e85d..e3aed3598 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -30,10 +30,9 @@ def test_build_templates(template, output_dir): output_dir=output_dir, ) - with Path(project_dir, 'requirements.txt').open() as f: - readme = f.read().splitlines() + readme = Path(project_dir, 'requirements.txt').read_text(encoding='utf-8') - assert readme == [ + assert readme.splitlines() == [ "pip", "Click", "pytest", From 9b80ba6786869c4be985f569507170be2a277c16 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:26:16 +0300 Subject: [PATCH 160/202] Tests: Use pathlib read_text/write_text (test_hooks.py) --- tests/test_hooks.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_hooks.py b/tests/test_hooks.py index ce4a2f426..9abd66af2 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -19,8 +19,7 @@ def make_test_repo(name, multiple_hooks=False): os.mkdir(hook_dir) os.mkdir(template) - with Path(template, 'README.rst').open('w') as f: - f.write("foo\n===\n\nbar\n") + Path(template, 'README.rst').write_text("foo\n===\n\nbar\n") with Path(hook_dir, 'pre_gen_project.py').open('w') as f: f.write("#!/usr/bin/env python\n") From f8ad02ae951009c0a167c42388d1f42709fe8cfd Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:26:43 +0300 Subject: [PATCH 161/202] Tests: Use pathlib read_text/write_text (test_output_folder.py) --- tests/test_output_folder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_output_folder.py b/tests/test_output_folder.py index daea7360c..00c4f7846 100644 --- a/tests/test_output_folder.py +++ b/tests/test_output_folder.py @@ -33,11 +33,11 @@ def test_output_folder(): something = """Hi! My name is Audrey Greenfeld. It is 2014.""" - something2 = Path('output_folder/something.txt').open().read() + something2 = Path('output_folder/something.txt').read_text() assert something == something2 in_folder = "The color is green and the letter is D." - in_folder2 = Path('output_folder/folder/in_folder.txt').open().read() + in_folder2 = Path('output_folder/folder/in_folder.txt').read_text() assert in_folder == in_folder2 assert os.path.isdir('output_folder/im_a.dir') From 258de5a419fa9ff719e15d23213443ddc9bfd5f9 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:27:22 +0300 Subject: [PATCH 162/202] Tests: Use pathlib read_text/write_text with encoding (test_default_extensions.py) --- tests/test_default_extensions.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_default_extensions.py b/tests/test_default_extensions.py index 13690097f..fc3bc08cb 100644 --- a/tests/test_default_extensions.py +++ b/tests/test_default_extensions.py @@ -26,8 +26,7 @@ def test_jinja2_time_extension(tmp_path): changelog_file = os.path.join(project_dir, 'HISTORY.rst') assert os.path.isfile(changelog_file) - with Path(changelog_file).open() as f: - changelog_lines = f.readlines() + changelog_lines = Path(changelog_file).open(encoding='utf-8').readlines() expected_lines = [ 'History\n', @@ -58,8 +57,7 @@ def test_jinja2_uuid_extension(tmp_path): changelog_file = os.path.join(project_dir, 'id') assert os.path.isfile(changelog_file) - with Path(changelog_file).open() as f: - changelog_lines = f.readlines() + changelog_lines = Path(changelog_file).open(encoding='utf-8').readlines() uuid.UUID(changelog_lines[0], version=4) assert True From b2be238d7414c7be05f6be7fdf2efedd12e80d55 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:28:01 +0300 Subject: [PATCH 163/202] Tests: Use pathlib read_text/write_text (test_generate_copy_without_render.py) --- tests/test_generate_copy_without_render.py | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/test_generate_copy_without_render.py b/tests/test_generate_copy_without_render.py index 5e4df7b3b..9e6039787 100644 --- a/tests/test_generate_copy_without_render.py +++ b/tests/test_generate_copy_without_render.py @@ -44,33 +44,33 @@ def test_generate_copy_without_render_extensions(): assert 'test_copy_without_render-not-rendered' in dir_contents assert 'test_copy_without_render-rendered' in dir_contents - with Path('test_copy_without_render/README.txt').open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + file_1 = Path('test_copy_without_render/README.txt').read_text() + assert '{{cookiecutter.render_test}}' in file_1 - with Path('test_copy_without_render/README.rst').open() as f: - assert 'I have been rendered!' in f.read() + file_2 = Path('test_copy_without_render/README.rst').read_text() + assert 'I have been rendered!' in file_2 - with Path( + file_3 = Path( 'test_copy_without_render/test_copy_without_render-rendered/README.txt' - ).open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + ).read_text() + assert '{{cookiecutter.render_test}}' in file_3 - with Path( + file_4 = Path( 'test_copy_without_render/test_copy_without_render-rendered/README.rst' - ).open() as f: - assert 'I have been rendered' in f.read() + ).read_text() + assert 'I have been rendered' in file_4 - with Path( + file_5 = Path( 'test_copy_without_render/' 'test_copy_without_render-not-rendered/' 'README.rst' - ).open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + ).read_text() + assert '{{cookiecutter.render_test}}' in file_5 - with Path('test_copy_without_render/rendered/not_rendered.yml').open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + file_6 = Path('test_copy_without_render/rendered/not_rendered.yml').read_text() + assert '{{cookiecutter.render_test}}' in file_6 - with Path( + file_7 = Path( 'test_copy_without_render/' 'test_copy_without_render-rendered/' 'README.md' - ).open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + ).read_text() + assert '{{cookiecutter.render_test}}' in file_7 From 8e9f138d474d34dbf84d7b4595f2a1452b7f4fd0 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:28:21 +0300 Subject: [PATCH 164/202] Tests: Use pathlib read_text/write_text (test_generate_copy_without_render_override.py) --- ...t_generate_copy_without_render_override.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/test_generate_copy_without_render_override.py b/tests/test_generate_copy_without_render_override.py index 1bda073d9..c2d836e3d 100644 --- a/tests/test_generate_copy_without_render_override.py +++ b/tests/test_generate_copy_without_render_override.py @@ -63,33 +63,33 @@ def test_generate_copy_without_render_extensions(): assert 'test_copy_without_render-not-rendered' in dir_contents assert 'test_copy_without_render-rendered' in dir_contents - with Path('test_copy_without_render/README.txt').open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + file_1 = Path('test_copy_without_render/README.txt').read_text() + assert '{{cookiecutter.render_test}}' in file_1 - with Path('test_copy_without_render/README.rst').open() as f: - assert 'I have been rendered!' in f.read() + file_2 = Path('test_copy_without_render/README.rst').read_text() + assert 'I have been rendered!' in file_2 - with Path( + file_3 = Path( 'test_copy_without_render/test_copy_without_render-rendered/README.txt' - ).open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + ).read_text() + assert '{{cookiecutter.render_test}}' in file_3 - with Path( + file_4 = Path( 'test_copy_without_render/test_copy_without_render-rendered/README.rst' - ).open() as f: - assert 'I have been rendered' in f.read() + ).read_text() + assert 'I have been rendered' in file_4 - with Path( + file_5 = Path( 'test_copy_without_render/' 'test_copy_without_render-not-rendered/' 'README.rst' - ).open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + ).read_text() + assert '{{cookiecutter.render_test}}' in file_5 - with Path('test_copy_without_render/rendered/not_rendered.yml').open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + file_6 = Path('test_copy_without_render/rendered/not_rendered.yml').read_text() + assert '{{cookiecutter.render_test}}' in file_6 - with Path( + file_7 = Path( 'test_copy_without_render/' 'test_copy_without_render-rendered/' 'README.md' - ).open() as f: - assert '{{cookiecutter.render_test}}' in f.read() + ).read_text() + assert '{{cookiecutter.render_test}}' in file_7 From 8413ed9a71940d7f3399da18b23c77c1cc3fb5dc Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:28:49 +0300 Subject: [PATCH 165/202] Tests: Use pathlib read_text/write_text with encoding (test_generate_file.py) --- tests/test_generate_file.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tests/test_generate_file.py b/tests/test_generate_file.py index 6e6604bea..9ff622168 100644 --- a/tests/test_generate_file.py +++ b/tests/test_generate_file.py @@ -46,9 +46,8 @@ def test_generate_file(env): env=env, ) assert os.path.isfile('tests/files/cheese.txt') - with Path('tests/files/cheese.txt').open() as f: - generated_text = f.read() - assert generated_text == 'Testing cheese' + generated_text = Path('tests/files/cheese.txt').read_text() + assert generated_text == 'Testing cheese' def test_generate_file_jsonify_filter(env): @@ -59,9 +58,8 @@ def test_generate_file_jsonify_filter(env): project_dir=".", infile=infile, context={'cookiecutter': data}, env=env ) assert os.path.isfile('tests/files/cheese.txt') - with Path('tests/files/cheese.txt').open() as f: - generated_text = f.read() - assert json.loads(generated_text) == data + generated_text = Path('tests/files/cheese.txt').read_text() + assert json.loads(generated_text) == data @pytest.mark.parametrize("length", (10, 40)) @@ -73,9 +71,8 @@ def test_generate_file_random_ascii_string(env, length, punctuation): context = {"cookiecutter": data, "length": length, "punctuation": punctuation} generate.generate_file(project_dir=".", infile=infile, context=context, env=env) assert os.path.isfile('tests/files/cheese.txt') - with Path('tests/files/cheese.txt').open() as f: - generated_text = f.read() - assert len(generated_text) == length + generated_text = Path('tests/files/cheese.txt').read_text() + assert len(generated_text) == length def test_generate_file_with_true_condition(env): @@ -93,9 +90,8 @@ def test_generate_file_with_true_condition(env): env=env, ) assert os.path.isfile('tests/files/cheese.txt') - with Path('tests/files/cheese.txt').open() as f: - generated_text = f.read() - assert generated_text == 'Testing that generate_file was y' + generated_text = Path('tests/files/cheese.txt').read_text() + assert generated_text == 'Testing that generate_file was y' def test_generate_file_with_false_condition(env): @@ -149,7 +145,7 @@ def test_generate_file_does_not_translate_lf_newlines_to_crlf(env, tmp_path): # this generated file should have a LF line ending gf = 'tests/files/cheese_lf_newlines.txt' - with Path(gf).open(newline='') as f: + with Path(gf).open(encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\n' assert f.newlines == '\n' @@ -167,7 +163,7 @@ def test_generate_file_does_not_translate_crlf_newlines_to_lf(env): # this generated file should have a CRLF line ending gf = 'tests/files/cheese_crlf_newlines.txt' - with Path(gf).open(newline='') as f: + with Path(gf).open(encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\r\n' assert f.newlines == '\r\n' From 9cdcdc0ae5622ac899daa5df772911cd0c80e5e2 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:29:10 +0300 Subject: [PATCH 166/202] Tests: Use pathlib read_text/write_text with encoding (test_generate_files.py) --- tests/test_generate_files.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/test_generate_files.py b/tests/test_generate_files.py index 436d83ac6..bb4075cae 100644 --- a/tests/test_generate_files.py +++ b/tests/test_generate_files.py @@ -44,7 +44,7 @@ def test_generate_files(tmp_path): assert simple_file.exists() assert simple_file.is_file() - simple_text = Path(simple_file).open().read() + simple_text = Path(simple_file).read_text(encoding='utf-8') assert simple_text == 'I eat pizzä' @@ -60,7 +60,7 @@ def test_generate_files_with_linux_newline(tmp_path): assert newline_file.is_file() assert newline_file.exists() - with Path(newline_file).open(newline='') as f: + with Path(newline_file).open(encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\n' assert f.newlines == '\n' @@ -83,7 +83,7 @@ def test_generate_files_with_jinja2_environment(tmp_path): assert conditions_file.is_file() assert conditions_file.exists() - simple_text = conditions_file.open('rt').read() + simple_text = conditions_file.read_text(encoding='utf-8') assert simple_text == 'I eat pizzä\n' @@ -100,7 +100,7 @@ def test_generate_files_with_trailing_newline_forced_to_linux_by_context(tmp_pat assert newline_file.is_file() assert newline_file.exists() - with Path(newline_file).open(newline='') as f: + with Path(newline_file).open(encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is LF\r\n' assert f.newlines == '\r\n' @@ -118,7 +118,7 @@ def test_generate_files_with_windows_newline(tmp_path): assert newline_file.is_file() assert newline_file.exists() - with Path(newline_file).open(newline='') as f: + with Path(newline_file).open(encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\r\n' assert f.newlines == '\r\n' @@ -136,7 +136,7 @@ def test_generate_files_with_windows_newline_forced_to_linux_by_context(tmp_path assert newline_file.is_file() assert newline_file.exists() - with Path(newline_file).open(newline='') as f: + with Path(newline_file).open(encoding='utf-8', newline='') as f: simple_text = f.readline() assert simple_text == 'newline is CRLF\n' @@ -202,7 +202,6 @@ def test_generate_files_permissions(tmp_path): output_dir=tmp_path, ) - assert Path(tmp_path, 'inputpermissions/simple.txt').exists() assert Path(tmp_path, 'inputpermissions/simple.txt').is_file() # Verify source simple.txt should still be 0o644 @@ -257,7 +256,7 @@ def test_generate_files_with_overwrite_if_exists_with_skip_if_file_exists(tmp_pa assert Path(simple_with_new_line_file).is_file() assert Path(simple_with_new_line_file).exists() - simple_text = Path(simple_file).open().read() + simple_text = Path(simple_file).read_text(encoding='utf-8') assert simple_text == 'temp' @@ -267,8 +266,7 @@ def test_generate_files_with_skip_if_file_exists(tmp_path): simple_with_new_line_file = Path(tmp_path, 'inputpizzä/simple-with-newline.txt') Path(tmp_path, 'inputpizzä').mkdir(parents=True) - with Path(simple_file).open('w') as f: - f.write('temp') + Path(simple_file).write_text('temp') with pytest.raises(exceptions.OutputDirExistsException): generate.generate_files( @@ -279,11 +277,10 @@ def test_generate_files_with_skip_if_file_exists(tmp_path): ) assert Path(simple_file).is_file() - assert Path(simple_file).exists() assert not Path(simple_with_new_line_file).is_file() assert not Path(simple_with_new_line_file).exists() - simple_text = Path(simple_file).open().read() + simple_text = Path(simple_file).read_text(encoding='utf-8') assert simple_text == 'temp' @@ -293,8 +290,7 @@ def test_generate_files_with_overwrite_if_exists(tmp_path): simple_with_new_line_file = Path(tmp_path, 'inputpizzä/simple-with-newline.txt') Path(tmp_path, 'inputpizzä').mkdir(parents=True) - with Path(simple_file).open('w') as f: - f.write('temp') + Path(simple_file).write_text('temp') generate.generate_files( context={'cookiecutter': {'food': 'pizzä'}}, @@ -308,7 +304,7 @@ def test_generate_files_with_overwrite_if_exists(tmp_path): assert Path(simple_with_new_line_file).is_file() assert Path(simple_with_new_line_file).exists() - simple_text = Path(simple_file).open().read() + simple_text = Path(simple_file).read_text(encoding='utf-8') assert simple_text == 'I eat pizzä' From b403dbec5165aa7153391e7012b103ac7cc25572 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:31:45 +0300 Subject: [PATCH 167/202] Tests: Use pathlib read_text/write_text with encoding(test_custom_extensions_in_hooks.py) --- tests/test_custom_extensions_in_hooks.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_custom_extensions_in_hooks.py b/tests/test_custom_extensions_in_hooks.py index ef9103021..b08225e44 100644 --- a/tests/test_custom_extensions_in_hooks.py +++ b/tests/test_custom_extensions_in_hooks.py @@ -39,7 +39,5 @@ def test_hook_with_extension(template, output_dir): extra_context={'project_slug': 'foobar', 'name': 'Cookiemonster'}, ) - with Path(project_dir, 'README.rst').open() as f: - readme = f.read().strip() - - assert readme == 'Hello Cookiemonster!' + readme = Path(project_dir, 'README.rst').read_text(encoding="utf8") + assert readme.strip() == 'Hello Cookiemonster!' From 2d4b630d618d238e9694b214f4338c30f8d16c5f Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:32:05 +0300 Subject: [PATCH 168/202] Tests: Use pathlib read_text/write_text (test_cookiecutter_local_no_input.py) --- tests/test_cookiecutter_local_no_input.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/test_cookiecutter_local_no_input.py b/tests/test_cookiecutter_local_no_input.py index eb1e52ed3..9e89c9539 100644 --- a/tests/test_cookiecutter_local_no_input.py +++ b/tests/test_cookiecutter_local_no_input.py @@ -66,9 +66,8 @@ def test_cookiecutter_no_input_return_rendered_file(): """Verify Jinja2 templating correctly works in `cookiecutter.json` file.""" project_dir = main.cookiecutter('tests/fake-repo-pre', no_input=True) assert project_dir == os.path.abspath('fake-project') - with Path(project_dir, 'README.rst').open() as fh: - contents = fh.read() - assert "Project name: **Fake Project**" in contents + content = Path(project_dir, 'README.rst').read_text() + assert "Project name: **Fake Project**" in content @pytest.mark.usefixtures('clean_system', 'remove_additional_dirs') @@ -77,11 +76,9 @@ def test_cookiecutter_dict_values_in_context(): project_dir = main.cookiecutter('tests/fake-repo-dict', no_input=True) assert project_dir == os.path.abspath('fake-project-dict') - with Path(project_dir, 'README.md').open() as fh: - contents = fh.read() - + content = Path(project_dir, 'README.md').read_text() assert ( - contents + content == textwrap.dedent( """ # README From 37da8d8bacf2b02fa21971848629852bc22c4d94 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 12:52:19 +0300 Subject: [PATCH 169/202] Convert 'format' usage to f-strings (cli.py) --- cookiecutter/cli.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cookiecutter/cli.py b/cookiecutter/cli.py index 4d92de749..ebc8dd0db 100644 --- a/cookiecutter/cli.py +++ b/cookiecutter/cli.py @@ -27,17 +27,16 @@ def version_msg(): """Return the Cookiecutter version, location and Python powering it.""" python_version = sys.version location = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - message = 'Cookiecutter %(version)s from {} (Python {})' - return message.format(location, python_version) + return f"Cookiecutter {__version__} from {location} (Python {python_version})" def validate_extra_context(ctx, param, value): """Validate extra context.""" - for s in value: - if '=' not in s: + for string in value: + if '=' not in string: raise click.BadParameter( - 'EXTRA_CONTEXT should contain items of the form key=value; ' - "'{}' doesn't match that form".format(s) + f"EXTRA_CONTEXT should contain items of the form key=value; " + f"'{string}' doesn't match that form" ) # Convert tuple -- e.g.: ('program_name=foobar', 'startsecs=66') @@ -51,8 +50,8 @@ def list_installed_templates(default_config, passed_config_file): cookiecutter_folder = config.get('cookiecutters_dir') if not os.path.exists(cookiecutter_folder): click.echo( - 'Error: Cannot list installed templates. Folder does not exist: ' - '{}'.format(cookiecutter_folder) + f"Error: Cannot list installed templates. " + f"Folder does not exist: {cookiecutter_folder}" ) sys.exit(-1) From 164caa4b03d1887676f6cf1a78faac8302d6c932 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 12:53:03 +0300 Subject: [PATCH 170/202] Convert 'format' usage to f-strings (generate.py) --- cookiecutter/generate.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index 01cbcf8a0..f71aba261 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -65,8 +65,8 @@ def apply_overwrites_to_context(context, overwrite_context): context_value.insert(0, overwrite) else: raise ValueError( - "{} provided for choice variable {}, but the " - "choices are {}.".format(overwrite, variable, context_value) + f"{overwrite} provided for choice variable {variable}, " + f"but the choices are {context_value}." ) elif isinstance(context_value, dict) and isinstance(overwrite, dict): # Partially overwrite some keys in original dict @@ -100,8 +100,8 @@ def generate_context( full_fpath = os.path.abspath(context_file) json_exc_message = str(e) our_exc_message = ( - 'JSON decoding error while loading "{}". Decoding' - ' error details: "{}"'.format(full_fpath, json_exc_message) + f"JSON decoding error while loading '{full_fpath}'. " + f"Decoding error details: '{json_exc_message}'" ) raise ContextDecodingException(our_exc_message) @@ -115,8 +115,8 @@ def generate_context( if default_context: try: apply_overwrites_to_context(obj, default_context) - except ValueError as ex: - warnings.warn("Invalid default received: " + str(ex)) + except ValueError as error: + warnings.warn(f"Invalid default received: {error}") if extra_context: apply_overwrites_to_context(obj, extra_context) From a1d48684c0a797a2b8ade2cadf99a67121917278 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 12:53:42 +0300 Subject: [PATCH 171/202] Convert 'format' usage to f-strings (prompt.py) --- cookiecutter/prompt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index d27481f7b..d556528e1 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -66,9 +66,9 @@ def read_user_choice(var_name, options): choice_lines = ['{} - {}'.format(*c) for c in choice_map.items()] prompt = '\n'.join( ( - f'Select {var_name}:', - '\n'.join(choice_lines), - 'Choose from {}'.format(', '.join(choices)), + f"Select {var_name}:", + "\n".join(choice_lines), + f"Choose from {', '.join(choices)}", ) ) From f4996f24fcf5215932859e24fcc23789c7c9b64e Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 12:53:56 +0300 Subject: [PATCH 172/202] Convert 'format' usage to f-strings (utils.py) --- cookiecutter/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookiecutter/utils.py b/cookiecutter/utils.py index 4750a2663..6796849fb 100644 --- a/cookiecutter/utils.py +++ b/cookiecutter/utils.py @@ -86,8 +86,8 @@ def prompt_and_delete(path, no_input=False): ok_to_delete = True else: question = ( - "You've downloaded {} before. Is it okay to delete and re-download it?" - ).format(path) + f"You've downloaded {path} before. Is it okay to delete and re-download it?" + ) ok_to_delete = read_user_yes_no(question, 'yes') From 282cf585d708fdc5f980b45edf70d644fcda936b Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 12:54:09 +0300 Subject: [PATCH 173/202] Convert 'format' usage to f-strings (zipfile.py) --- cookiecutter/zipfile.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cookiecutter/zipfile.py b/cookiecutter/zipfile.py index 0342f1d71..241944e62 100644 --- a/cookiecutter/zipfile.py +++ b/cookiecutter/zipfile.py @@ -63,8 +63,7 @@ def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None): first_filename = zip_file.namelist()[0] if not first_filename.endswith('/'): raise InvalidZipRepository( - 'Zip repository {} does not include ' - 'a top-level directory'.format(zip_uri) + f"Zip repository {zip_uri} does not include a top-level directory" ) # Construct the final target directory From b789202854d101d873c2c4373a0983326617ddae Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:46:30 +0300 Subject: [PATCH 174/202] Remove encoding (test_templates.py) --- tests/test_templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_templates.py b/tests/test_templates.py index e3aed3598..44b9475d2 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -30,7 +30,7 @@ def test_build_templates(template, output_dir): output_dir=output_dir, ) - readme = Path(project_dir, 'requirements.txt').read_text(encoding='utf-8') + readme = Path(project_dir, 'requirements.txt').read_text() assert readme.splitlines() == [ "pip", From c0c2d5cfcf8287665bfb15f93a0e8dcdbd59001e Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 16:46:54 +0300 Subject: [PATCH 175/202] Fix encoding typo (test_custom_extensions_in_hooks.py) --- tests/test_custom_extensions_in_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_custom_extensions_in_hooks.py b/tests/test_custom_extensions_in_hooks.py index b08225e44..9f13733ba 100644 --- a/tests/test_custom_extensions_in_hooks.py +++ b/tests/test_custom_extensions_in_hooks.py @@ -39,5 +39,5 @@ def test_hook_with_extension(template, output_dir): extra_context={'project_slug': 'foobar', 'name': 'Cookiemonster'}, ) - readme = Path(project_dir, 'README.rst').read_text(encoding="utf8") + readme = Path(project_dir, 'README.rst').read_text(encoding="utf-8") assert readme.strip() == 'Hello Cookiemonster!' From 1e821d2945d29e575a6a65e1850b910b34885924 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 17:16:50 +0300 Subject: [PATCH 176/202] Use touch instead of open('w') (test_determine_repo_dir_finds_existing_cookiecutter.py) Co-authored-by: alkatar21 <61387986+alkatar21@users.noreply.github.com> --- .../test_determine_repo_dir_finds_existing_cookiecutter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/repository/test_determine_repo_dir_finds_existing_cookiecutter.py b/tests/repository/test_determine_repo_dir_finds_existing_cookiecutter.py index ebd7df320..3810668f6 100644 --- a/tests/repository/test_determine_repo_dir_finds_existing_cookiecutter.py +++ b/tests/repository/test_determine_repo_dir_finds_existing_cookiecutter.py @@ -21,7 +21,7 @@ def cloned_cookiecutter_path(user_config_data, template): cloned_template_path = os.path.join(cookiecutters_dir, template) os.mkdir(cloned_template_path) - Path(cloned_template_path, "cookiecutter.json").open('w') # creates file + Path(cloned_template_path, "cookiecutter.json").touch() # creates file return cloned_template_path From 0fe509c30c83ace5215211204275c73f928a2236 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 17:17:16 +0300 Subject: [PATCH 177/202] Use touch instead of open('w') (test_determine_repo_dir_finds_subdirectories.py) Co-authored-by: alkatar21 <61387986+alkatar21@users.noreply.github.com> --- .../repository/test_determine_repo_dir_finds_subdirectories.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/repository/test_determine_repo_dir_finds_subdirectories.py b/tests/repository/test_determine_repo_dir_finds_subdirectories.py index a1f7a034c..bcea1b387 100644 --- a/tests/repository/test_determine_repo_dir_finds_subdirectories.py +++ b/tests/repository/test_determine_repo_dir_finds_subdirectories.py @@ -25,7 +25,7 @@ def cloned_cookiecutter_path(user_config_data, template): subdir_template_path = os.path.join(cloned_template_path, 'my-dir') if not os.path.exists(subdir_template_path): os.mkdir(subdir_template_path) - Path(subdir_template_path, 'cookiecutter.json').open('w') # creates file + Path(subdir_template_path, 'cookiecutter.json').touch() # creates file return subdir_template_path From 8a731d1e21db8150c741d5e6e393fc2c0827b6bf Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 18:02:44 +0300 Subject: [PATCH 178/202] Include caused exception details on reraise (PEP 3134) (environment.py) --- cookiecutter/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookiecutter/environment.py b/cookiecutter/environment.py index f2804c595..02e2abf40 100644 --- a/cookiecutter/environment.py +++ b/cookiecutter/environment.py @@ -34,7 +34,7 @@ def __init__(self, **kwargs): try: super().__init__(extensions=extensions, **kwargs) except ImportError as err: - raise UnknownExtension(f'Unable to load extension: {err}') + raise UnknownExtension(f'Unable to load extension: {err}') from err def _read_extensions(self, context): """Return list of extensions as str to be passed on to the Jinja2 env. From 0c94173df7e7ca876fe88fb23372452fecc9fff4 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 18:02:55 +0300 Subject: [PATCH 179/202] Include caused exception details on reraise (PEP 3134) (generate.py) --- cookiecutter/generate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index f71aba261..16d218c93 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -103,7 +103,7 @@ def generate_context( f"JSON decoding error while loading '{full_fpath}'. " f"Decoding error details: '{json_exc_message}'" ) - raise ContextDecodingException(our_exc_message) + raise ContextDecodingException(our_exc_message) from e # Add the Python object to the context dictionary file_name = os.path.split(context_file)[1] @@ -302,7 +302,7 @@ def generate_files( ) except UndefinedError as err: msg = f"Unable to create project directory '{unrendered_dir}'" - raise UndefinedVariableInTemplate(msg, err, context) + raise UndefinedVariableInTemplate(msg, err, context) from err # We want the Jinja path and the OS paths to match. Consequently, we'll: # + CD to the template folder @@ -371,7 +371,7 @@ def generate_files( rmtree(project_dir) _dir = os.path.relpath(unrendered_dir, output_dir) msg = f"Unable to create directory '{_dir}'" - raise UndefinedVariableInTemplate(msg, err, context) + raise UndefinedVariableInTemplate(msg, err, context) from err for f in files: infile = os.path.normpath(os.path.join(root, f)) @@ -393,7 +393,7 @@ def generate_files( if delete_project_on_failure: rmtree(project_dir) msg = f"Unable to create file '{infile}'" - raise UndefinedVariableInTemplate(msg, err, context) + raise UndefinedVariableInTemplate(msg, err, context) from err if accept_hooks: _run_hook_from_repo_dir( From 1c09d347e9ef1911a589edb78a2ec01eaadc3180 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 18:03:06 +0300 Subject: [PATCH 180/202] Include caused exception details on reraise (PEP 3134) (hooks.py) --- cookiecutter/hooks.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookiecutter/hooks.py b/cookiecutter/hooks.py index 763287c58..5b08e7d70 100644 --- a/cookiecutter/hooks.py +++ b/cookiecutter/hooks.py @@ -85,12 +85,12 @@ def run_script(script_path, cwd='.'): raise FailedHookException( f'Hook script failed (exit status: {exit_status})' ) - except OSError as os_error: - if os_error.errno == errno.ENOEXEC: + except OSError as err: + if err.errno == errno.ENOEXEC: raise FailedHookException( 'Hook script failed, might be an empty file or missing a shebang' - ) - raise FailedHookException(f'Hook script failed (error: {os_error})') + ) from err + raise FailedHookException(f'Hook script failed (error: {err})') from err def run_script_with_context(script_path, cwd, context): From 7992483dc526375f3c2776fbebfe89f83f58746b Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 18:03:16 +0300 Subject: [PATCH 181/202] Include caused exception details on reraise (PEP 3134) (vcs.py) --- cookiecutter/vcs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py index 93ca2366a..e1a99e9bc 100644 --- a/cookiecutter/vcs.py +++ b/cookiecutter/vcs.py @@ -114,12 +114,12 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): raise RepositoryNotFound( f'The repository {repo_url} could not be found, ' 'have you made a typo?' - ) + ) from clone_error if any(error in output for error in BRANCH_ERRORS): raise RepositoryCloneFailed( f'The {checkout} branch of repository ' f'{repo_url} could not found, have you made a typo?' - ) + ) from clone_error logger.error('git clone failed with error: %s', output) raise From 86fc66f937adb6ae9d03b305a82bbf22334709a6 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 18:03:29 +0300 Subject: [PATCH 182/202] Include caused exception details on reraise (PEP 3134) (prompt.py) --- cookiecutter/prompt.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index d556528e1..ee19a339b 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -92,9 +92,9 @@ def process_json(user_value, default_value=None): try: user_dict = json.loads(user_value, object_pairs_hook=OrderedDict) - except Exception: + except Exception as error: # Leave it up to click to ask the user again - raise click.UsageError('Unable to decode to JSON.') + raise click.UsageError('Unable to decode to JSON.') from error if not isinstance(user_dict, dict): # Leave it up to click to ask the user again @@ -159,8 +159,7 @@ def render_variable(env, raw, cookiecutter_dict): template = env.from_string(raw) - rendered_template = template.render(cookiecutter=cookiecutter_dict) - return rendered_template + return template.render(cookiecutter=cookiecutter_dict) def prompt_choice_for_config(cookiecutter_dict, env, key, options, no_input): @@ -219,7 +218,7 @@ def prompt_for_config(context, no_input=False): cookiecutter_dict[key] = val except UndefinedError as err: msg = f"Unable to render variable '{key}'" - raise UndefinedVariableInTemplate(msg, err, context) + raise UndefinedVariableInTemplate(msg, err, context) from err # Second pass; handle the dictionaries. for key, raw in context['cookiecutter'].items(): @@ -238,6 +237,6 @@ def prompt_for_config(context, no_input=False): cookiecutter_dict[key] = val except UndefinedError as err: msg = f"Unable to render variable '{key}'" - raise UndefinedVariableInTemplate(msg, err, context) + raise UndefinedVariableInTemplate(msg, err, context) from err return cookiecutter_dict From b1d470135ef27427ac8800d2dab1a8ddafef1b0d Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Wed, 8 Jun 2022 18:13:47 +0300 Subject: [PATCH 183/202] Fix not closed files handlers --- tests/test_default_extensions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_default_extensions.py b/tests/test_default_extensions.py index 2b03b7b23..d229e3d8c 100644 --- a/tests/test_default_extensions.py +++ b/tests/test_default_extensions.py @@ -26,7 +26,8 @@ def test_jinja2_time_extension(tmp_path): changelog_file = os.path.join(project_dir, 'HISTORY.rst') assert os.path.isfile(changelog_file) - changelog_lines = Path(changelog_file).open(encoding='utf-8').readlines() + with Path(changelog_file).open(encoding='utf-8') as f: + changelog_lines = f.readlines() expected_lines = [ 'History\n', @@ -57,6 +58,7 @@ def test_jinja2_uuid_extension(tmp_path): changelog_file = os.path.join(project_dir, 'id') assert os.path.isfile(changelog_file) - changelog_lines = Path(changelog_file).open(encoding='utf-8').readlines() + with Path(changelog_file).open(encoding='utf-8') as f: + changelog_lines = f.readlines() uuid.UUID(changelog_lines[0], version=4) From e162629ea6308e542520342d1337cbcc328f8b9b Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 00:29:06 +0300 Subject: [PATCH 184/202] Extend sphinx config with type annotations from typehints --- docs/conf.py | 4 ++++ docs/requirements.txt | 1 + 2 files changed, 5 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 332153029..9c2ffdaf4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,6 +55,7 @@ 'sphinx_click.ext', 'myst_parser', 'sphinxcontrib.apidoc', + 'sphinx_autodoc_typehints', ] # Add any paths that contain templates here, relative to this directory. @@ -369,3 +370,6 @@ apidoc_output_dir = "." apidoc_toc_file = False apidoc_extra_args = ["-t", "_templates"] + +autodoc_member_order = "groupwise" +autodoc_typehints = "none" diff --git a/docs/requirements.txt b/docs/requirements.txt index cd980601c..582a2052e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -4,3 +4,4 @@ myst-parser>=0.17.2 sphinx-autobuild>=2021.3.14 Sphinx>=4.5.0 sphinxcontrib-apidoc>=0.3.0 +sphinx-autodoc-typehints>=1.18.2 \ No newline at end of file From 2e2691ddfacff5a228d083d675d07e1ad77fad2d Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 00:29:47 +0300 Subject: [PATCH 185/202] Find refactored and type annotated --- cookiecutter/find.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/cookiecutter/find.py b/cookiecutter/find.py index 054e286f4..526fd5adf 100644 --- a/cookiecutter/find.py +++ b/cookiecutter/find.py @@ -1,31 +1,27 @@ """Functions for finding Cookiecutter templates and other components.""" import logging import os +from pathlib import Path from cookiecutter.exceptions import NonTemplatedInputDirException logger = logging.getLogger(__name__) -def find_template(repo_dir): - """Determine which child directory of `repo_dir` is the project template. +def find_template(repo_dir: os.PathLike[str]) -> Path: + """Determine 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. + :return: Relative path to project template. """ logger.debug('Searching %s for the project template.', 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 + for str_path in os.listdir(repo_dir): + if 'cookiecutter' in str_path and '{{' in str_path and '}}' in str_path: + project_template = Path(repo_dir, str_path) break - - if project_template: - project_template = os.path.join(repo_dir, project_template) - logger.debug('The project template appears to be %s', project_template) - return project_template else: raise NonTemplatedInputDirException + + logger.debug('The project template appears to be %s', project_template) + return project_template From aebdc499127e1cfa350e1235c333b20c1f444c9e Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 00:30:26 +0300 Subject: [PATCH 186/202] Update findpath tests --- tests/test_find.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_find.py b/tests/test_find.py index 761c02235..affbd806e 100644 --- a/tests/test_find.py +++ b/tests/test_find.py @@ -1,5 +1,5 @@ """Tests for `cookiecutter.find` module.""" -import os +from pathlib import Path import pytest @@ -9,12 +9,12 @@ @pytest.fixture(params=['fake-repo-pre', 'fake-repo-pre2']) def repo_dir(request): """Fixture returning path for `test_find_template` test.""" - return os.path.join('tests', request.param) + return Path('tests', request.param) def test_find_template(repo_dir): """Verify correctness of `find.find_template` path detection.""" template = find.find_template(repo_dir=repo_dir) - test_dir = os.path.join(repo_dir, '{{cookiecutter.repo_name}}') + test_dir = Path(repo_dir, '{{cookiecutter.repo_name}}') assert template == test_dir From 2a8731490517a48f1bbfe3c466431dea93bf3191 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 01:28:21 +0300 Subject: [PATCH 187/202] Compatibility fix --- cookiecutter/find.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookiecutter/find.py b/cookiecutter/find.py index 526fd5adf..409e4ce9a 100644 --- a/cookiecutter/find.py +++ b/cookiecutter/find.py @@ -8,7 +8,7 @@ logger = logging.getLogger(__name__) -def find_template(repo_dir: os.PathLike[str]) -> Path: +def find_template(repo_dir: "os.PathLike[str]") -> Path: """Determine which child directory of ``repo_dir`` is the project template. :param repo_dir: Local directory of newly cloned repo. From 4d127f87e565dbb4b39513dfd5fc91161e32cb4b Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 03:36:05 +0300 Subject: [PATCH 188/202] Simplify make_sure_path_exists function --- cookiecutter/utils.py | 20 +++++++++----------- tests/test_utils.py | 4 ++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/cookiecutter/utils.py b/cookiecutter/utils.py index 6796849fb..e8709a8e8 100644 --- a/cookiecutter/utils.py +++ b/cookiecutter/utils.py @@ -1,15 +1,16 @@ """Helper functions used throughout Cookiecutter.""" import contextlib -import errno import logging import os import shutil import stat import sys +from pathlib import Path -from cookiecutter.prompt import read_user_yes_no from jinja2.ext import Extension +from cookiecutter.prompt import read_user_yes_no + logger = logging.getLogger(__name__) @@ -31,19 +32,16 @@ def rmtree(path): shutil.rmtree(path, onerror=force_delete) -def make_sure_path_exists(path): +def make_sure_path_exists(path: "os.PathLike[str]") -> None: """Ensure that a directory exists. - :param path: A directory path. + :param path: A directory tree path for creation. """ - logger.debug('Making sure path exists: %s', path) + logger.debug('Making sure path exists (Create tree if not exist): %s', path) try: - os.makedirs(path) - logger.debug('Created directory at: %s', path) - except OSError as exception: - if exception.errno != errno.EEXIST: - return False - return True + Path(path).mkdir(parents=True, exist_ok=True) + except OSError as error: + raise OSError(f'Unable to create replay directory at {path}') from error @contextlib.contextmanager diff --git a/tests/test_utils.py b/tests/test_utils.py index 5e93d5ad4..247bea7af 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -49,8 +49,8 @@ def test_make_sure_path_exists(tmp_path): existing_directory = tmp_path directory_to_create = Path(tmp_path, "not_yet_created") - assert utils.make_sure_path_exists(existing_directory) - assert utils.make_sure_path_exists(directory_to_create) + utils.make_sure_path_exists(existing_directory) + utils.make_sure_path_exists(directory_to_create) # Ensure by base system methods. assert existing_directory.is_dir() From 4cae4233f89b8458b785c62db5feadc5454d5d56 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 03:39:48 +0300 Subject: [PATCH 189/202] Type annotate vcs.clone function --- cookiecutter/vcs.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cookiecutter/vcs.py b/cookiecutter/vcs.py index e1a99e9bc..8e61417ac 100644 --- a/cookiecutter/vcs.py +++ b/cookiecutter/vcs.py @@ -2,7 +2,9 @@ import logging import os import subprocess # nosec +from pathlib import Path from shutil import which +from typing import Optional from cookiecutter.exceptions import ( RepositoryCloneFailed, @@ -54,7 +56,12 @@ def is_vcs_installed(repo_type): return bool(which(repo_type)) -def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): +def clone( + repo_url: str, + checkout: Optional[str] = None, + clone_to_dir: "os.PathLike[str]" = ".", + no_input: bool = False, +): """Clone a repo to the current directory. :param repo_url: Repo URL of unknown type. @@ -66,7 +73,7 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False): :returns: str with path to the new directory of the repository. """ # Ensure that clone_to_dir exists - clone_to_dir = os.path.expanduser(clone_to_dir) + clone_to_dir = Path(clone_to_dir).expanduser() make_sure_path_exists(clone_to_dir) # identify the repo_type From 886cc8207c07b94d38d6ff3b600cc47037cb43cb Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 03:40:39 +0300 Subject: [PATCH 190/202] Remove strings from vcs.clone tests --- tests/vcs/test_clone.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/vcs/test_clone.py b/tests/vcs/test_clone.py index bd19ef1ab..ab9598ed8 100644 --- a/tests/vcs/test_clone.py +++ b/tests/vcs/test_clone.py @@ -28,11 +28,11 @@ def test_clone_should_rstrip_trailing_slash_in_repo_url(mocker, clone_dir): autospec=True, ) - vcs.clone('https://github.com/foo/bar/', clone_to_dir=str(clone_dir), no_input=True) + vcs.clone('https://github.com/foo/bar/', clone_to_dir=clone_dir, no_input=True) mock_subprocess.assert_called_once_with( ['git', 'clone', 'https://github.com/foo/bar'], - cwd=str(clone_dir), + cwd=clone_dir, stderr=subprocess.STDOUT, ) @@ -114,13 +114,13 @@ def test_clone_should_invoke_vcs_command( branch = 'foobar' repo_dir = vcs.clone( - repo_url, checkout=branch, clone_to_dir=str(clone_dir), no_input=True + repo_url, checkout=branch, clone_to_dir=clone_dir, no_input=True ) assert repo_dir == expected_repo_dir mock_subprocess.assert_any_call( - [repo_type, 'clone', repo_url], cwd=str(clone_dir), stderr=subprocess.STDOUT + [repo_type, 'clone', repo_url], cwd=clone_dir, stderr=subprocess.STDOUT ) branch_info = [branch] From 68cdeb8f3d719cd777b27b6cf9f9a7fbf533543f Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 03:42:16 +0300 Subject: [PATCH 191/202] Move OSError raising out of replay.dump (raise in make_sure_path_exists) --- cookiecutter/replay.py | 5 ++--- tests/replay/test_dump.py | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cookiecutter/replay.py b/cookiecutter/replay.py index 9730e84da..d3c989879 100644 --- a/cookiecutter/replay.py +++ b/cookiecutter/replay.py @@ -16,10 +16,9 @@ def get_file_name(replay_dir, template_name): return os.path.join(replay_dir, file_name) -def dump(replay_dir, template_name, context): +def dump(replay_dir: "os.PathLike[str]", template_name: str, context: dict): """Write json data to file.""" - if not make_sure_path_exists(replay_dir): - raise OSError(f'Unable to create replay dir at {replay_dir}') + make_sure_path_exists(replay_dir) if not isinstance(template_name, str): raise TypeError('Template name is required to be of type str') diff --git a/tests/replay/test_dump.py b/tests/replay/test_dump.py index c757321b1..57ad8ee74 100644 --- a/tests/replay/test_dump.py +++ b/tests/replay/test_dump.py @@ -57,7 +57,9 @@ def mock_ensure_failure(mocker): Used to mock internal function and limit test scope. Always return expected value: False """ - return mocker.patch('cookiecutter.replay.make_sure_path_exists', return_value=False) + return mocker.patch( + 'cookiecutter.replay.make_sure_path_exists', side_effect=OSError + ) @pytest.fixture @@ -72,7 +74,7 @@ def mock_ensure_success(mocker): def test_ioerror_if_replay_dir_creation_fails(mock_ensure_failure, replay_test_dir): """Test that replay.dump raises when the replay_dir cannot be created.""" - with pytest.raises(IOError): + with pytest.raises(OSError): replay.dump(replay_test_dir, 'foo', {'cookiecutter': {'hello': 'world'}}) mock_ensure_failure.assert_called_once_with(replay_test_dir) From d958bdcbdfefa294c440fdd01485ca71ec3c2353 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 03:43:25 +0300 Subject: [PATCH 192/202] Type annotate generate.render_and_create_dir for correct passing to make_sure_path_exists --- cookiecutter/generate.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py index 16d218c93..251d5f439 100644 --- a/cookiecutter/generate.py +++ b/cookiecutter/generate.py @@ -6,9 +6,9 @@ import shutil import warnings from collections import OrderedDict - +from pathlib import Path from binaryornot.check import is_binary -from jinja2 import FileSystemLoader +from jinja2 import FileSystemLoader, Environment from jinja2.exceptions import TemplateSyntaxError, UndefinedError from cookiecutter.environment import StrictEnvironment @@ -203,19 +203,23 @@ def generate_file(project_dir, infile, context, env, skip_if_file_exists=False): def render_and_create_dir( - dirname, context, output_dir, environment, overwrite_if_exists=False + dirname: str, + context: dict, + output_dir: "os.PathLike[str]", + environment: Environment, + overwrite_if_exists: bool = False, ): """Render name of a directory, create the directory, return its path.""" name_tmpl = environment.from_string(dirname) rendered_dirname = name_tmpl.render(**context) - dir_to_create = os.path.normpath(os.path.join(output_dir, rendered_dirname)) + dir_to_create = Path(output_dir, rendered_dirname) logger.debug( 'Rendered dir %s must exist in output_dir %s', dir_to_create, output_dir ) - output_dir_exists = os.path.exists(dir_to_create) + output_dir_exists = dir_to_create.exists() if output_dir_exists: if overwrite_if_exists: From 16051ff104188c40cf550c2a7b66fdb229fc27d9 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 03:47:47 +0300 Subject: [PATCH 193/202] Type annotate zipfile.unzip --- cookiecutter/zipfile.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cookiecutter/zipfile.py b/cookiecutter/zipfile.py index 241944e62..bab90a264 100644 --- a/cookiecutter/zipfile.py +++ b/cookiecutter/zipfile.py @@ -1,6 +1,8 @@ """Utility functions for handling and fetching repo archives in zip format.""" import os import tempfile +from pathlib import Path +from typing import Optional from zipfile import BadZipFile, ZipFile import requests @@ -10,7 +12,13 @@ from cookiecutter.utils import make_sure_path_exists, prompt_and_delete -def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None): +def unzip( + zip_uri: str, + is_url: bool, + clone_to_dir: "os.PathLike[str]" = ".", + no_input: bool = False, + password: Optional[str] = None, +): """Download and unpack a zipfile at a given URI. This will download the zipfile to the cookiecutter repository, @@ -25,7 +33,7 @@ def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None): :param password: The password to use when unpacking the repository. """ # Ensure that clone_to_dir exists - clone_to_dir = os.path.expanduser(clone_to_dir) + clone_to_dir = Path(clone_to_dir).expanduser() make_sure_path_exists(clone_to_dir) if is_url: From 759a6ac466dd06e24da318b331633390a672298e Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 03:56:35 +0300 Subject: [PATCH 194/202] Update make_sure_path_exists tests --- tests/test_utils.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 247bea7af..f540442c0 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -65,14 +65,10 @@ def test_make_sure_path_exists_correctly_handle_os_error(mocker): Should return True if directory exist or created. Should return False if impossible to create directory (for example protected) """ - - def raiser(*args, **kwargs): - raise OSError() - - mocker.patch("os.makedirs", raiser) - uncreatable_directory = Path('protected_path') - - assert not utils.make_sure_path_exists(uncreatable_directory) + mocker.patch("pathlib.Path.mkdir", side_effect=OSError) + with pytest.raises(OSError) as err: + utils.make_sure_path_exists(Path('protected_path')) + assert str(err.value) == "Unable to create replay directory at protected_path" def test_work_in(tmp_path): From 7322abda4c696033ede36daacb64044663de3806 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 03:59:11 +0300 Subject: [PATCH 195/202] Fix exception text --- cookiecutter/utils.py | 2 +- tests/test_utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookiecutter/utils.py b/cookiecutter/utils.py index e8709a8e8..813f62cb1 100644 --- a/cookiecutter/utils.py +++ b/cookiecutter/utils.py @@ -41,7 +41,7 @@ def make_sure_path_exists(path: "os.PathLike[str]") -> None: try: Path(path).mkdir(parents=True, exist_ok=True) except OSError as error: - raise OSError(f'Unable to create replay directory at {path}') from error + raise OSError(f'Unable to create directory at {path}') from error @contextlib.contextmanager diff --git a/tests/test_utils.py b/tests/test_utils.py index f540442c0..fdd3692e2 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -68,7 +68,7 @@ def test_make_sure_path_exists_correctly_handle_os_error(mocker): mocker.patch("pathlib.Path.mkdir", side_effect=OSError) with pytest.raises(OSError) as err: utils.make_sure_path_exists(Path('protected_path')) - assert str(err.value) == "Unable to create replay directory at protected_path" + assert str(err.value) == "Unable to create directory at protected_path" def test_work_in(tmp_path): From b0a9ebb642b6348607c24ce9fd8ad6e74c0105c5 Mon Sep 17 00:00:00 2001 From: Andrey Shpak Date: Thu, 9 Jun 2022 12:28:57 +0300 Subject: [PATCH 196/202] Update cookiecutter/utils.py Co-authored-by: Jens W. Klein --- cookiecutter/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookiecutter/utils.py b/cookiecutter/utils.py index 813f62cb1..d90b3a81d 100644 --- a/cookiecutter/utils.py +++ b/cookiecutter/utils.py @@ -37,7 +37,7 @@ def make_sure_path_exists(path: "os.PathLike[str]") -> None: :param path: A directory tree path for creation. """ - logger.debug('Making sure path exists (Create tree if not exist): %s', path) + logger.debug('Making sure path exists (creates tree if not exist): %s', path) try: Path(path).mkdir(parents=True, exist_ok=True) except OSError as error: From f57420816a3c0359adf5280eb2f3cbcbd29ec08d Mon Sep 17 00:00:00 2001 From: Alex Blackwell Date: Sun, 19 Jun 2022 05:13:32 -0700 Subject: [PATCH 197/202] Easy PR! Fix typos and add minor doc updates (#1741) Fix typos and add minor doc updates --- cookiecutter/exceptions.py | 2 +- cookiecutter/prompt.py | 2 +- cookiecutter/repository.py | 2 +- docs/tutorials/tutorial2.rst | 2 +- setup.cfg | 2 +- tests/conftest.py | 11 ++--------- tests/test_cli.py | 5 +---- 7 files changed, 8 insertions(+), 18 deletions(-) diff --git a/cookiecutter/exceptions.py b/cookiecutter/exceptions.py index 4acf6dc47..622e7c6ef 100644 --- a/cookiecutter/exceptions.py +++ b/cookiecutter/exceptions.py @@ -132,7 +132,7 @@ def __str__(self): class UnknownExtension(CookiecutterException): """ - Exception for un-importable extention. + Exception for un-importable extension. Raised when an environment is unable to import a required extension. """ diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py index ee19a339b..f4c19c97a 100644 --- a/cookiecutter/prompt.py +++ b/cookiecutter/prompt.py @@ -222,7 +222,7 @@ def prompt_for_config(context, no_input=False): # Second pass; handle the dictionaries. for key, raw in context['cookiecutter'].items(): - # Skip private type dicts not ot be rendered. + # Skip private type dicts not to be rendered. if key.startswith('_') and not key.startswith('__'): continue diff --git a/cookiecutter/repository.py b/cookiecutter/repository.py index 5086e9b01..e407910fc 100644 --- a/cookiecutter/repository.py +++ b/cookiecutter/repository.py @@ -87,7 +87,7 @@ def determine_repo_dir( :param password: The password to use when extracting the repository. :param directory: Directory within repo where cookiecutter.json lives. :return: A tuple containing the cookiecutter template directory, and - a boolean descriving whether that directory should be cleaned up + a boolean describing whether that directory should be cleaned up after the template has been instantiated. :raises: `RepositoryNotFound` if a repository directory could not be found. """ diff --git a/docs/tutorials/tutorial2.rst b/docs/tutorials/tutorial2.rst index 202412537..d16f6b7fa 100644 --- a/docs/tutorials/tutorial2.rst +++ b/docs/tutorials/tutorial2.rst @@ -89,7 +89,7 @@ You can expect similar output: project_slug [test_web]: author [Anonymous]: Cookiecutter Developer -Resulting directory should be inside your work directory with a name that matches `project_slug` you defined. Inside that direcory there should be `index.html` with generated source: +Resulting directory should be inside your work directory with a name that matches `project_slug` you defined. Inside that directory there should be `index.html` with generated source: .. code-block:: html diff --git a/setup.cfg b/setup.cfg index 938b39de6..f32b1830b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,6 +16,6 @@ testpaths = tests addopts = -vvv --cov-report term-missing --cov=cookiecutter [doc8] -# TODO: Remove current max-line-lengh ignore in follow-up and adopt black limit. +# TODO: Remove current max-line-length ignore in follow-up and adopt black limit. # max-line-length = 88 ignore = D001 diff --git a/tests/conftest.py b/tests/conftest.py index e21071304..0990c07d3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,10 +12,6 @@ cookiecutters_dir: '{cookiecutters_dir}' replay_dir: '{replay_dir}' """ -# In YAML, double quotes mean to use escape sequences. -# Single quotes mean we will have unescaped backslahes. -# http://blogs.perl.org/users/tinita/2018/03/ -# strings-in-yaml---to-quote-or-not-to-quote.html @pytest.fixture(autouse=True) @@ -38,7 +34,7 @@ def backup_dir(original_dir, backup_dir): if not os.path.isdir(original_dir): return False - # Remove existing backups before backing up. If they exist, they're stale. + # Remove existing stale backups before backing up. if os.path.isdir(backup_dir): utils.rmtree(backup_dir) @@ -48,12 +44,9 @@ def backup_dir(original_dir, backup_dir): def restore_backup_dir(original_dir, backup_dir, original_dir_found): """Restore default contents.""" - # 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 + # Delete original_dir if a backup exists if original_dir_is_dir and os.path.isdir(backup_dir): utils.rmtree(original_dir) else: diff --git a/tests/test_cli.py b/tests/test_cli.py index 39e924093..e0308f06e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -543,10 +543,7 @@ def test_debug_list_installed_templates(cli_runner, debug_file, user_config_path """Verify --list-installed command correct invocation.""" fake_template_dir = os.path.dirname(os.path.abspath('fake-project')) os.makedirs(os.path.dirname(user_config_path)) - # In YAML, double quotes mean to use escape sequences. - # Single quotes mean we will have unescaped backslahes. - # http://blogs.perl.org/users/tinita/2018/03/ - # strings-in-yaml---to-quote-or-not-to-quote.html + # Single quotes in YAML will not parse escape codes (\). Path(user_config_path).write_text(f"cookiecutters_dir: '{fake_template_dir}'") Path("fake-project", "cookiecutter.json").write_text('{}') From 60a049ebf0014d0fd88567b95539025d174bdd42 Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Mon, 20 Jun 2022 00:23:21 +1200 Subject: [PATCH 198/202] Remove universal bdist_wheel option; use "python -m build" (#1739) --- Makefile | 8 ++++---- setup.cfg | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 0392aeb33..293aedef6 100644 --- a/Makefile +++ b/Makefile @@ -99,19 +99,19 @@ submodules: ## Pull and update git submodules recursively .PHONY: release release: clean ## Package and upload release @echo "+ $@" - @python setup.py sdist bdist_wheel + @python -m build @twine upload -r $(PYPI_SERVER) dist/* .PHONY: sdist sdist: clean ## Build sdist distribution @echo "+ $@" - @python setup.py sdist + @python -m build --sdist @ls -l dist .PHONY: wheel -wheel: clean ## Build bdist_wheel distribution +wheel: clean ## Build wheel distribution @echo "+ $@" - @python setup.py bdist_wheel + @python -m build --wheel @ls -l dist .PHONY: help diff --git a/setup.cfg b/setup.cfg index f32b1830b..3f29fe9a0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,9 +8,6 @@ statistics = 1 # black official is 88 max-line-length = 88 -[bdist_wheel] -universal = 1 - [tool:pytest] testpaths = tests addopts = -vvv --cov-report term-missing --cov=cookiecutter From dc95dd25c41fe8a2edd221606b6a16e95ab084e3 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Mon, 4 Jul 2022 02:37:09 +1000 Subject: [PATCH 199/202] docs: fix simple typo, shat -> that (#1749) There is a small typo in docs/conf.py. Should read `that` rather than `shat`. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 9c2ffdaf4..562baddb0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -327,7 +327,7 @@ # 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. +# HTML files that should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_post_files = [] From e9b3b84125d7cc71c858604c12ceea70e881883f Mon Sep 17 00:00:00 2001 From: segunb Date: Mon, 1 Aug 2022 01:17:25 +0100 Subject: [PATCH 200/202] Fixed minor typos in docs (#1753) Authored-by: Segun Babalola --- docs/installation.rst | 2 +- docs/overview.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 068c6d9d9..995fd077b 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -14,7 +14,7 @@ Python interpreter Install Python for your operating system. On Windows and macOS this is usually necessary. -Most Linux distributions are coming with Python pre-installed. +Most Linux distributions come with Python pre-installed. Consult the official `Python documentation `_ for details. You can install the Python binaries from `python.org `_. diff --git a/docs/overview.rst b/docs/overview.rst index 6c75f77fe..026cb1bb1 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -3,13 +3,13 @@ Overview ======== Cookiecutter takes a template provided as a directory structure with template-files. -Templates can be in located in the filesystem, as a ZIP-file or on a VCS-Server (Git/Hg) like GitHub. +Templates can be located in the filesystem, as a ZIP-file or on a VCS-Server (Git/Hg) like GitHub. It reads a settings file and prompts the user interactively whether or not to change the settings. Then it takes both and generates an output directory structure from it. -Additional the template can provide code (Python or shell-script) to be executed before and after generation (pre-gen- and post-gen-hooks). +Additionally the template can provide code (Python or shell-script) to be executed before and after generation (pre-gen- and post-gen-hooks). Input From cf81d63bf3d82e1739db73bcbed6f1012890e33e Mon Sep 17 00:00:00 2001 From: Thomas Meckel <14177833+tmeckel@users.noreply.github.com> Date: Fri, 9 Sep 2022 14:04:10 +0200 Subject: [PATCH 201/202] feat: Add resolved template repository path as _repo_dir to the context (#1771) * Changes to cookiecutter/main.py: * Add resolved template repository path as _repo_dir to the context * Changes to tests/test_cli.py: * corrected test test_echo_undefined_variable_error to check for new context item _repo_dir Co-authored-by: Thomas Meckel --- cookiecutter/main.py | 3 +++ tests/test_cli.py | 1 + 2 files changed, 4 insertions(+) diff --git a/cookiecutter/main.py b/cookiecutter/main.py index cb81a0b94..ba1df1b6c 100644 --- a/cookiecutter/main.py +++ b/cookiecutter/main.py @@ -109,6 +109,9 @@ def cookiecutter( # include template dir or url in the context dict context['cookiecutter']['_template'] = template + # include repo dir or url in the context dict + context['cookiecutter']['_repo_dir'] = repo_dir + # include output+dir in the context dict context['cookiecutter']['_output_dir'] = os.path.abspath(output_dir) diff --git a/tests/test_cli.py b/tests/test_cli.py index e0308f06e..0364e2cef 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -398,6 +398,7 @@ def test_echo_undefined_variable_error(output_dir, cli_runner): 'github_username': 'hackebrot', 'project_slug': 'testproject', '_template': template_path, + '_repo_dir': template_path, '_output_dir': output_dir, } } From 678b4480578a8d58ddf31c024c74ee849186c771 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 19 Sep 2022 10:50:12 +0100 Subject: [PATCH 202/202] Enable py311 support - enable py311 pipelines - expose py311 support in metadata --- .github/workflows/tests.yml | 21 ++++++++++++++------- noxfile.py | 4 ++-- setup.py | 1 + tox.ini | 1 + 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a84a59992..054c01526 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -52,11 +52,18 @@ jobs: - ubuntu-latest - macos-latest - windows-latest - python: - - "3.7" - - "3.8" - - "3.9" - - "3.10" + include: + - nox_env: py37 + python: "3.7" + - nox_env: py38 + python: "3.8" + - nox_env: py39 + python: "3.9" + - nox_env: py310 + python: "3.10" + - nox_env: py311 + python: "~3.11.0-0" # needed until 3.11 is released + steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python }} @@ -68,9 +75,9 @@ jobs: python -m pip install --upgrade pip pip install nox virtualenv - name: Project internals test build - run: "nox -p ${{ matrix.python }} -s tests" + run: "nox -p ${{ matrix.nox_env }} -s tests" - name: Project security test - run: "nox -p ${{ matrix.python }} -s safety_tests" + run: "nox -p ${{ matrix.nox_env }} -s safety_tests" - name: Send coverage report to codeclimate continue-on-error: true uses: paambaati/codeclimate-action@v3.0.0 diff --git a/noxfile.py b/noxfile.py index 25189967e..d003a2c7f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -24,7 +24,7 @@ def lint(session): session.run("pre-commit", "run", "-a") -@nox.session(python=["3.7", "3.8", "3.9", "3.10"]) +@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11"]) def tests(session): """Run test suite with pytest.""" session = base_install(session) @@ -37,7 +37,7 @@ def tests(session): ) -@nox.session(python=["3.7", "3.8", "3.9", "3.10"]) +@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11"]) def safety_tests(session): """Run safety tests.""" session = base_install(session) diff --git a/setup.py b/setup.py index d61fbd6ba..acd5e9755 100644 --- a/setup.py +++ b/setup.py @@ -49,6 +49,7 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python", diff --git a/tox.ini b/tox.ini index 4cde29296..8c6e075d5 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,7 @@ envlist = py38 py39 py310 + py311 minversion = 3.14.2 requires = virtualenv >= 20.4.5