├── pytest.ini ├── docs ├── readme.rst ├── invoke.rst ├── index.rst ├── console_script_setup.rst ├── travis_pypi_setup.rst ├── prompts.rst ├── pypi_release_checklist.rst ├── tutorial.rst └── conf.py ├── {{cookiecutter.project_slug}} ├── docs │ ├── authors.rst │ ├── history.rst │ ├── readme.rst │ ├── contributing.rst │ ├── usage.rst │ ├── index.rst │ ├── installation.rst │ └── conf.py ├── tests │ ├── __init__.py │ └── test_{{cookiecutter.project_slug}}.py ├── {{cookiecutter.project_slug}} │ ├── {{cookiecutter.project_slug}}.py │ ├── __init__.py │ └── cli.py ├── HISTORY.rst ├── AUTHORS.rst ├── .editorconfig ├── .bumpversion.cfg ├── .github │ ├── ISSUE_TEMPLATE.md │ └── workflows │ │ └── ci.yml ├── .gitignore ├── README.rst ├── pyproject.toml ├── CONTRIBUTING.rst ├── tasks.py └── LICENSE ├── .bumpversion.cfg ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ └── ci.yml ├── hooks ├── pre_gen_project.py └── post_gen_project.py ├── .editorconfig ├── pyproject.toml ├── cookiecutter.json ├── tasks.py ├── LICENSE ├── .gitignore ├── README.rst ├── CONTRIBUTING.rst ├── tests └── test_bake_project.py └── poetry.lock /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = tests/ 3 | -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | .. _readme: 2 | 3 | .. include:: ../README.rst 4 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docs/history.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../HISTORY.rst 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docs/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit test package for {{ cookiecutter.project_slug }}.""" 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}.py: -------------------------------------------------------------------------------- 1 | """Main module.""" 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/HISTORY.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | History 3 | ======= 4 | 5 | {{ cookiecutter.version }} ({% now 'local' %}) 6 | ------------------ 7 | 8 | * First release on PyPI. 9 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docs/usage.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Usage 3 | ===== 4 | 5 | To use {{ cookiecutter.project_name }} in a project:: 6 | 7 | import {{ cookiecutter.project_slug }} 8 | -------------------------------------------------------------------------------- /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.2.0 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:pyproject.toml] 7 | search = version = "{current_version}" 8 | replace = version = "{new_version}" 9 | 10 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py: -------------------------------------------------------------------------------- 1 | """Top-level package for {{ cookiecutter.project_name }}.""" 2 | 3 | __author__ = """{{ cookiecutter.full_name }}""" 4 | __email__ = '{{ cookiecutter.email }}' 5 | __version__ = '{{ cookiecutter.version }}' 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Development Lead 6 | ---------------- 7 | 8 | * {{ cookiecutter.full_name }} <{{ cookiecutter.email }}> 9 | 10 | Contributors 11 | ------------ 12 | 13 | None yet. Why not be the first? 14 | -------------------------------------------------------------------------------- /docs/invoke.rst: -------------------------------------------------------------------------------- 1 | Invoke 2 | ====== 3 | 4 | The generated project is ready to run some useful tasks like formatting, linting, testing. 5 | 6 | To do this we use pyinvoke_ to wrap up the required commands. 7 | 8 | .. _pyinvoke: http://www.pyinvoke.org/ 9 | 10 | Execute `invoke --list` to see the list of available commands. 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * Date you used Cookiecutter PyPackage: 2 | * Cookiecutter version used, if any: 3 | * Python version, if any: 4 | * Operating System: 5 | 6 | ### Description 7 | 8 | Describe what you were trying to get done. Tell us what happened, what went wrong, and what you expected to happen. 9 | 10 | ### What I Did 11 | 12 | ``` 13 | Paste the command(s) you ran and the output. 14 | ``` 15 | -------------------------------------------------------------------------------- /hooks/pre_gen_project.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | 4 | 5 | MODULE_REGEX = r'^[_a-zA-Z][_a-zA-Z0-9]+$' 6 | 7 | module_name = '{{ cookiecutter.project_slug}}' 8 | 9 | if not re.match(MODULE_REGEX, module_name): 10 | print('ERROR: The project slug (%s) is not a valid Python module name. Please do not use a - and use _ instead' % module_name) 11 | 12 | #Exit to cancel project 13 | sys.exit(1) -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = {{ cookiecutter.version }} 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:pyproject.toml] 7 | search = version = "{current_version}" 8 | replace = version = "{new_version}" 9 | 10 | [bumpversion:file:{{ cookiecutter.project_slug }}/__init__.py] 11 | search = __version__ = '{current_version}' 12 | replace = __version__ = '{new_version}' 13 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * {{ cookiecutter.project_name }} version: 2 | * Python version: 3 | * Operating System: 4 | 5 | ### Description 6 | 7 | Describe what you were trying to get done. 8 | Tell us what happened, what went wrong, and what you expected to happen. 9 | 10 | ### What I Did 11 | 12 | ``` 13 | Paste the command(s) you ran and the output. 14 | If there was a crash, please include the traceback here. 15 | ``` 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | 23 | [*.{diff,patch}] 24 | trim_trailing_whitespace = false 25 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to {{ cookiecutter.project_name }}'s documentation! 2 | ====================================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | readme 9 | installation 10 | usage 11 | modules 12 | contributing 13 | {% if cookiecutter.create_author_file == 'y' -%}authors 14 | {% endif -%}history 15 | 16 | Indices and tables 17 | ================== 18 | * :ref:`genindex` 19 | * :ref:`modindex` 20 | * :ref:`search` 21 | -------------------------------------------------------------------------------- /hooks/post_gen_project.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | 4 | PROJECT_DIRECTORY = os.path.realpath(os.path.curdir) 5 | 6 | 7 | def remove_file(filepath): 8 | os.remove(os.path.join(PROJECT_DIRECTORY, filepath)) 9 | 10 | 11 | if __name__ == '__main__': 12 | 13 | if '{{ cookiecutter.create_author_file }}' != 'y': 14 | remove_file('AUTHORS.rst') 15 | remove_file('docs/authors.rst') 16 | 17 | if 'no' in '{{ cookiecutter.command_line_interface|lower }}': 18 | cli_file = os.path.join('{{ cookiecutter.project_slug }}', 'cli.py') 19 | remove_file(cli_file) 20 | 21 | if 'Not open source' == '{{ cookiecutter.open_source_license }}': 22 | remove_file('LICENSE') 23 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/cli.py: -------------------------------------------------------------------------------- 1 | """Console script for {{cookiecutter.project_slug}}.""" 2 | 3 | import sys 4 | 5 | {% if cookiecutter.command_line_interface|lower == 'click' -%} 6 | import click 7 | {%- endif %} 8 | 9 | {% if cookiecutter.command_line_interface|lower == 'click' %} 10 | @click.command() 11 | def main(): 12 | """Console script for {{cookiecutter.project_slug}}.""" 13 | click.echo("Replace this message by putting your code into " 14 | "{{cookiecutter.project_slug}}.cli.main") 15 | click.echo("See click documentation at https://click.palletsprojects.com/") 16 | return 0 17 | {%- endif %} 18 | 19 | 20 | if __name__ == "__main__": 21 | sys.exit(main()) # pragma: no cover 22 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool] 2 | [tool.poetry] 3 | name = "cookiecutter-pypackage" 4 | version = "0.2.0" 5 | description = "Cookiecutter template for a Python package" 6 | license = "BSD-3-Clause" 7 | authors = ["Sam Briggs "] 8 | readme = "README.rst" 9 | repository = "https://github.com/briggySmalls/cookiecutter-pypackage" 10 | documentation = "https://briggysmalls.github.io/cookiecutter-pypackage" 11 | keywords = ['cookiecutter', 'template', 'package'] 12 | 13 | [tool.poetry.dependencies] 14 | python = ">=3.7,<4" 15 | 16 | [tool.poetry.dev-dependencies] 17 | bump2version = "*" 18 | cookiecutter = "*" 19 | invocations = "*" 20 | invoke = "*" 21 | pytest = ">=7.2.0" 22 | pytest-cookies = "*" 23 | sphinx = ">=5.3.0" 24 | sphinx-rtd-theme = "*" 25 | pyyaml = "^5.3.1" 26 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "full_name": "Sam Briggs", 3 | "email": "briggySmalls@example.com", 4 | "github_username": "briggySmalls", 5 | "project_name": "Python Boilerplate", 6 | "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}", 7 | "project_short_description": "Python Boilerplate contains all the boilerplate you need to create a Python package.", 8 | "pypi_username": "{{ cookiecutter.github_username }}", 9 | "version": "0.1.0", 10 | "use_pytest": "y", 11 | "use_pypi_deployment_with_travis": "y", 12 | "add_pyup_badge": "n", 13 | "command_line_interface": ["Click", "No command-line interface"], 14 | "create_author_file": "y", 15 | "open_source_license": ["MIT", "BSD-3-Clause", "ISC", "Apache-2.0", "GPL-3.0-only", "Not open source"], 16 | "use_google_docstrings": "y", 17 | "_copy_without_render": [ 18 | "*.github/workflows/*.yml" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. cookiecutter-pypackage documentation master file, created by 2 | sphinx-quickstart on Sun Dec 13 09:13:01 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to cookiecutter-pypackage's documentation! 7 | ================================================== 8 | 9 | Getting Started 10 | --------------- 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | readme 16 | tutorial 17 | pypi_release_checklist 18 | 19 | Basics 20 | ------ 21 | 22 | .. toctree:: 23 | :maxdepth: 2 24 | 25 | prompts 26 | invoke 27 | 28 | Advanced Features 29 | ----------------- 30 | 31 | .. toctree:: 32 | :maxdepth: 2 33 | 34 | travis_pypi_setup 35 | console_script_setup 36 | 37 | 38 | Indices and tables 39 | ================== 40 | 41 | * :ref:`genindex` 42 | * :ref:`modindex` 43 | * :ref:`search` 44 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | """Development tasks for the cookiecutter template project""" 2 | 3 | import webbrowser 4 | from pathlib import Path 5 | import platform 6 | from invoke import task 7 | 8 | ROOT_DIR = Path(__file__).parent 9 | DOCS_DIR = ROOT_DIR.joinpath('docs') 10 | DOCS_BUILD_DIR = DOCS_DIR.joinpath('_build') 11 | DOCS_INDEX = DOCS_BUILD_DIR.joinpath('index.html') 12 | 13 | 14 | def _run(c, command): 15 | return c.run(command, pty=platform.system() != 'Windows') 16 | 17 | 18 | @task 19 | def test(c): 20 | """ 21 | Run tests 22 | """ 23 | _run(c, "pytest") 24 | 25 | 26 | @task 27 | def docs(c): 28 | """ 29 | Generate documentation 30 | """ 31 | _run(c, "sphinx-build -b html {} {}".format(DOCS_DIR, DOCS_BUILD_DIR)) 32 | webbrowser.open(DOCS_INDEX.absolute().as_uri()) 33 | 34 | 35 | @task 36 | def clean_docs(c): 37 | """ 38 | Clean up files from documentation builds 39 | """ 40 | _run(c, "rm -fr {}".format(DOCS_BUILD_DIR)) 41 | -------------------------------------------------------------------------------- /docs/console_script_setup.rst: -------------------------------------------------------------------------------- 1 | .. _console-script-setup: 2 | 3 | 4 | Console Script Setup 5 | ==================== 6 | 7 | Optionally, your package can include a console script using Click or argparse (Python 3.2+). 8 | 9 | How It Works 10 | ------------ 11 | 12 | If the 'command_line_interface' option is set to ['click'] or ['argparse'] during setup, cookiecutter will 13 | add a file 'cli.py' in the project_slug subdirectory. An entry point is added to 14 | pyproject.toml that points to the main function in cli.py. 15 | 16 | Usage 17 | ------------ 18 | To use the console script in development: 19 | 20 | .. code-block:: bash 21 | 22 | pip install -e projectdir 23 | 24 | 'projectdir' should be the top level project directory with the pyproject.toml file 25 | 26 | The script will be generated with output for no arguments and --help. 27 | 28 | --help 29 | show help menu and exit 30 | 31 | More Details 32 | ------------ 33 | 34 | You can read more about Click at: 35 | http://click.pocoo.org/ 36 | -------------------------------------------------------------------------------- /docs/travis_pypi_setup.rst: -------------------------------------------------------------------------------- 1 | .. _travis-pypi-setup: 2 | 3 | 4 | Travis/PyPI Setup 5 | ================= 6 | 7 | Optionally, your package can automatically be released on PyPI whenever you 8 | push a new tag to the master branch. 9 | 10 | Install the Travis CLI tool 11 | ---------------------------- 12 | 13 | This is OS-specific. 14 | 15 | ### macOS 16 | 17 | We recommend the Homebrew travis package: 18 | 19 | ``` 20 | brew install travis 21 | ``` 22 | 23 | ### Windows and Linux 24 | 25 | Follow the official Travis CLI installationinstructions for your operating system: 26 | 27 | https://github.com/travis-ci/travis.rb#installation 28 | 29 | How It Works 30 | ------------ 31 | 32 | Once you have the `travis` command - line tool installed, from the root of your project do:: 33 | 34 | travis encrypt --add deploy.password 35 | 36 | This will encrypt your locally-stored PyPI password and save that to your `.travis.yml` file. Commit that change to git. 37 | 38 | 39 | Your Release Process 40 | -------------------- 41 | 42 | If you are using this feature, this is how you would do a patch release: 43 | 44 | .. code-block:: bash 45 | 46 | bump2version patch 47 | git push --tags 48 | 49 | This will result in: 50 | 51 | * mypackage 0.1.1 showing up in your GitHub tags/releases page 52 | * mypackage 0.1.1 getting released on PyPI 53 | 54 | You can also replace patch with `minor` or `major`. 55 | 56 | 57 | More Details 58 | ------------ 59 | 60 | You can read more about using Travis for PyPI deployment at: 61 | https://docs.travis-ci.com/user/deployment/pypi/ 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Audrey Roy Greenfeld and individual contributors. 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Audrey Roy Greenfeld nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | 8 | Stable release 9 | -------------- 10 | 11 | To install {{ cookiecutter.project_name }}, run this command in your terminal: 12 | 13 | .. code-block:: console 14 | 15 | $ pip install {{ cookiecutter.project_slug }} 16 | 17 | This is the preferred method to install {{ cookiecutter.project_name }}, as it will always install the most recent stable release. 18 | 19 | If you don't have `pip`_ installed, this `Python installation guide`_ can guide 20 | you through the process. 21 | 22 | .. _pip: https://pip.pypa.io 23 | .. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ 24 | 25 | 26 | From source 27 | ----------- 28 | 29 | The source for {{ cookiecutter.project_name }} can be downloaded from the `Github repo`_. 30 | 31 | You can either clone the public repository: 32 | 33 | .. code-block:: console 34 | 35 | $ git clone git://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }} 36 | 37 | Or download the `tarball`_: 38 | 39 | .. code-block:: console 40 | 41 | $ curl -OJL https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/tarball/master 42 | 43 | Once you have a copy of the source, you can install it with: 44 | 45 | .. code-block:: console 46 | 47 | $ pip install . 48 | 49 | .. _Github repo: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }} 50 | .. _tarball: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/tarball/master 51 | -------------------------------------------------------------------------------- /docs/prompts.rst: -------------------------------------------------------------------------------- 1 | Prompts 2 | ======= 3 | 4 | When you create a package, you are prompted to enter these values. 5 | 6 | Templated Values 7 | ---------------- 8 | 9 | The following appear in various parts of your generated project. 10 | 11 | full_name 12 | Your full name. 13 | 14 | email 15 | Your email address. 16 | 17 | github_username 18 | Your GitHub username. 19 | 20 | project_name 21 | The name of your new Python package project. This is used in documentation, so spaces and any characters are fine here. 22 | 23 | project_slug 24 | The namespace of your Python package. This should be Python import-friendly. Typically, it is the slugified version of project_name. 25 | 26 | project_short_description 27 | A 1-sentence description of what your Python package does. 28 | 29 | release_date 30 | The date of the first release. 31 | 32 | pypi_username 33 | Your Python Package Index account username. 34 | 35 | year 36 | The year of the initial package copyright in the license file. 37 | 38 | version 39 | The starting version number of the package. 40 | 41 | Options 42 | ------- 43 | 44 | The following package configuration options set up different features for your project. 45 | 46 | command_line_interface 47 | Whether to create a console script using Click. Console script entry point will match the project_slug. Options: ['Click', "No command-line interface"] 48 | 49 | use_google_docstrings 50 | Whether to support parsing docstrings in the `Google Python Style Guide`_ format. 51 | 52 | .. _Google Python Style Guide: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings 53 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | .venv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | 104 | # IDE settings 105 | .vscode/ -------------------------------------------------------------------------------- /docs/pypi_release_checklist.rst: -------------------------------------------------------------------------------- 1 | PyPI Release Checklist 2 | ====================== 3 | 4 | For Every Release 5 | ------------------- 6 | 7 | #. Update HISTORY.rst 8 | 9 | #. Commit the changes: 10 | 11 | .. code-block:: bash 12 | 13 | git add HISTORY.rst 14 | git commit -m "Changelog for upcoming release 0.1.1." 15 | 16 | #. Update version number (can also be patch or major) 17 | 18 | .. code-block:: bash 19 | 20 | bump2version minor 21 | 22 | #. Run the tests: 23 | 24 | .. code-block:: bash 25 | 26 | tox 27 | 28 | #. Push the commit: 29 | 30 | .. code-block:: bash 31 | 32 | git push 33 | 34 | #. Push the tags, creating the new release on both GitHub and PyPI: 35 | 36 | .. code-block:: bash 37 | 38 | git push --tags 39 | 40 | #. Check the PyPI listing page to make sure that the README, release notes, and roadmap display properly. If not, try one of these: 41 | 42 | #. Copy and paste the RestructuredText into http://rst.ninjs.org/ to find out what broke the formatting. 43 | 44 | #. Check your long_description locally: 45 | 46 | .. code-block:: bash 47 | 48 | pip install readme_renderer 49 | # Replace PROBLEM.rst with the name of the file you are having trouble with 50 | python -m readme_renderer PROBLEM.rst >/dev/null 51 | 52 | #. Edit the release on GitHub (e.g. https://github.com/audreyr/cookiecutter/releases). Paste the release notes into the release's release page, and come up with a title for the release. 53 | 54 | About This Checklist 55 | -------------------- 56 | 57 | This checklist is adapted from: 58 | 59 | * https://gist.github.com/audreyr/5990987 60 | * https://gist.github.com/audreyr/9f1564ea049c14f682f4 61 | 62 | It assumes that you are using all features of Cookiecutter PyPackage. 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # OSX useful to ignore 7 | *.DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Thumbnails 12 | ._* 13 | 14 | # Files that might appear in the root of a volume 15 | .DocumentRevisions-V100 16 | .fseventsd 17 | .Spotlight-V100 18 | .TemporaryItems 19 | .Trashes 20 | .VolumeIcon.icns 21 | .com.apple.timemachine.donotpresent 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | # C extensions 31 | *.so 32 | 33 | # Distribution / packaging 34 | .Python 35 | env/ 36 | build/ 37 | develop-eggs/ 38 | dist/ 39 | downloads/ 40 | eggs/ 41 | .eggs/ 42 | lib/ 43 | lib64/ 44 | parts/ 45 | sdist/ 46 | var/ 47 | *.egg-info/ 48 | .installed.cfg 49 | *.egg 50 | 51 | # PyInstaller 52 | # Usually these files are written by a python script from a template 53 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 54 | *.manifest 55 | *.spec 56 | 57 | # Installer logs 58 | pip-log.txt 59 | pip-delete-this-directory.txt 60 | 61 | # Unit test / coverage reports 62 | htmlcov/ 63 | .tox/ 64 | .coverage 65 | .coverage.* 66 | .cache 67 | nosetests.xml 68 | coverage.xml 69 | *,cover 70 | .hypothesis/ 71 | .pytest_cache/ 72 | 73 | # Translations 74 | *.mo 75 | *.pot 76 | 77 | # Django stuff: 78 | *.log 79 | 80 | # Sphinx documentation 81 | docs/_build/ 82 | 83 | # IntelliJ Idea family of suites 84 | .idea 85 | *.iml 86 | ## File-based project format: 87 | *.ipr 88 | *.iws 89 | ## mpeltonen/sbt-idea plugin 90 | .idea_modules/ 91 | 92 | # PyBuilder 93 | target/ 94 | 95 | # Cookiecutter 96 | output/ 97 | python_boilerplate/ 98 | cookiecutter-pypackage-env/ 99 | 100 | # IDE settings 101 | .vscode/ -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/README.rst: -------------------------------------------------------------------------------- 1 | {% set is_open_source = cookiecutter.open_source_license != 'Not open source' -%} 2 | {% for _ in cookiecutter.project_name %}={% endfor %} 3 | {{ cookiecutter.project_name }} 4 | {% for _ in cookiecutter.project_name %}={% endfor %} 5 | 6 | {% if is_open_source %} 7 | .. image:: https://img.shields.io/pypi/v/{{ cookiecutter.project_slug }}.svg 8 | :target: https://pypi.python.org/pypi/{{ cookiecutter.project_slug }} 9 | 10 | .. image:: https://img.shields.io/travis/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}.svg 11 | :target: https://travis-ci.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }} 12 | 13 | .. image:: https://readthedocs.org/projects/{{ cookiecutter.project_slug | replace("_", "-") }}/badge/?version=latest 14 | :target: https://{{ cookiecutter.project_slug | replace("_", "-") }}.readthedocs.io/en/latest/?badge=latest 15 | :alt: Documentation Status 16 | {%- endif %} 17 | 18 | {% if cookiecutter.add_pyup_badge == 'y' %} 19 | .. image:: https://pyup.io/repos/github/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/shield.svg 20 | :target: https://pyup.io/repos/github/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/ 21 | :alt: Updates 22 | {% endif %} 23 | 24 | 25 | {{ cookiecutter.project_short_description }} 26 | 27 | {% if is_open_source %} 28 | * Free software: {{ cookiecutter.open_source_license }} 29 | * Documentation: https://{{ cookiecutter.project_slug | replace("_", "-") }}.readthedocs.io. 30 | {% endif %} 31 | 32 | Features 33 | -------- 34 | 35 | * TODO 36 | 37 | Credits 38 | ------- 39 | 40 | This package was created with Cookiecutter_ and the `briggySmalls/cookiecutter-pypackage`_ project template. 41 | 42 | .. _Cookiecutter: https://github.com/audreyr/cookiecutter 43 | .. _`briggySmalls/cookiecutter-pypackage`: https://github.com/briggySmalls/cookiecutter-pypackage 44 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/pyproject.toml: -------------------------------------------------------------------------------- 1 | {%- set license_classifiers = { 2 | 'MIT': 'License :: OSI Approved :: MIT License', 3 | 'BSD-3-Clause': 'License :: OSI Approved :: BSD License', 4 | 'ISC': 'License :: OSI Approved :: ISC License (ISCL)', 5 | 'Apache-2.0': 'License :: OSI Approved :: Apache Software License', 6 | 'GPL-3.0-only': 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)' 7 | } -%} 8 | [tool] 9 | [tool.poetry] 10 | name = "{{ cookiecutter.project_slug }}" 11 | version = "{{ cookiecutter.version }}" 12 | homepage = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}" 13 | description = "Top-level package for {{ cookiecutter.project_name }}." 14 | authors = ["{{ cookiecutter.full_name.replace('\"', '\\\"') }} <{{ cookiecutter.email }}>"] 15 | readme = "README.rst" 16 | {%- if cookiecutter.open_source_license in license_classifiers %} 17 | license = "{{ cookiecutter.open_source_license }}" 18 | {%- endif %} 19 | classifiers=[ 20 | 'Development Status :: 2 - Pre-Alpha', 21 | 'Intended Audience :: Developers', 22 | {%- if cookiecutter.open_source_license in license_classifiers %} 23 | '{{ license_classifiers[cookiecutter.open_source_license] }}', 24 | {%- endif %} 25 | 'Natural Language :: English', 26 | 'Programming Language :: Python :: 3', 27 | 'Programming Language :: Python :: 3.5', 28 | 'Programming Language :: Python :: 3.6', 29 | 'Programming Language :: Python :: 3.7', 30 | 'Programming Language :: Python :: 3.8', 31 | ] 32 | packages = [ 33 | { include = "{{ cookiecutter.project_slug }}" }, 34 | { include = "tests", format = "sdist" }, 35 | ] 36 | 37 | [tool.poetry.dependencies] 38 | python = ">=3.7,<4" 39 | {%- if cookiecutter.command_line_interface|lower == 'click' %} 40 | click = "*" 41 | {%- endif %} 42 | 43 | [tool.poetry.dev-dependencies] 44 | bumpversion = "*" 45 | coverage = "*" 46 | flake8 = "*" 47 | invoke = "*" 48 | isort = "*" 49 | pylint = "*" 50 | {% if cookiecutter.use_pytest == 'y' -%} 51 | pytest = ">=7.2.0"{% endif %} 52 | sphinx = ">=5.3.0" 53 | black = "*" 54 | 55 | {% if cookiecutter.command_line_interface|lower == 'click' -%} 56 | [tool.poetry.scripts] 57 | {{ cookiecutter.project_slug }} = '{{ cookiecutter.project_slug }}.cli:main' 58 | {%- endif %} 59 | 60 | [build-system] 61 | requires = ["poetry>=0.12"] 62 | build-backend = "poetry.masonry.api" 63 | 64 | [tool.isort] 65 | profile = "black" 66 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | push: 7 | branches: 8 | - master 9 | jobs: 10 | test: 11 | strategy: 12 | matrix: 13 | python-version: ["3.10"] 14 | os: [ubuntu-latest] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - name: Check out repository 18 | uses: actions/checkout@v3 19 | - name: "Set up python ${{ matrix.python-version }}" 20 | id: setup-python 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | #---------------------------------------------- 25 | # ----- install & configure poetry ----- 26 | #---------------------------------------------- 27 | - name: Install Poetry 28 | uses: snok/install-poetry@v1 29 | with: 30 | virtualenvs-create: true 31 | virtualenvs-in-project: true 32 | #---------------------------------------------- 33 | # load cached venv if cache exists 34 | #---------------------------------------------- 35 | - name: Load cached venv 36 | id: cached-poetry-dependencies 37 | uses: actions/cache@v3 38 | with: 39 | path: .venv 40 | key: venv-${{ matrix.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} 41 | #---------------------------------------------- 42 | # install dependencies if cache does not exist 43 | #---------------------------------------------- 44 | - name: Install dependencies 45 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' 46 | run: poetry install --no-interaction --no-root 47 | #---------------------------------------------- 48 | # install your root project, if required 49 | #---------------------------------------------- 50 | - name: Install library 51 | run: poetry install --no-interaction 52 | #---------------------------------------------- 53 | # Run tests 54 | #---------------------------------------------- 55 | - name: Run tests 56 | run: poetry run invoke test 57 | #---------------------------------------------- 58 | # Build docs 59 | #---------------------------------------------- 60 | - name: Build docs 61 | run: poetry run invoke docs 62 | #---------------------------------------------- 63 | # Deploy docs (if on master) 64 | #---------------------------------------------- 65 | - name: Deploy docs 🚀 66 | if: github.ref == 'refs/heads/master' 67 | uses: JamesIves/github-pages-deploy-action@v4 68 | with: 69 | folder: docs/_build -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/tests/test_{{cookiecutter.project_slug}}.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Tests for `{{ cookiecutter.project_slug }}` package.""" 3 | # pylint: disable=redefined-outer-name 4 | 5 | {% if cookiecutter.use_pytest == 'y' -%} 6 | import pytest 7 | {%- else -%} 8 | import unittest 9 | {%- endif %} 10 | {% if cookiecutter.command_line_interface | lower == 'click' -%} 11 | from click.testing import CliRunner 12 | {%- endif %} 13 | 14 | {% if cookiecutter.command_line_interface | lower == 'click' -%} 15 | from {{ cookiecutter.project_slug }} import cli 16 | {%- endif %} 17 | 18 | {%- if cookiecutter.use_pytest == 'y' %} 19 | 20 | 21 | @pytest.fixture 22 | def response(): 23 | """Sample pytest fixture. 24 | 25 | See more at: http://doc.pytest.org/en/latest/fixture.html 26 | """ 27 | # import requests 28 | # return requests.get('https://github.com/audreyr/cookiecutter-pypackage') 29 | 30 | 31 | def test_content(response): 32 | """Sample pytest test function with the pytest fixture as an argument.""" 33 | # from bs4 import BeautifulSoup 34 | # assert 'GitHub' in BeautifulSoup(response.content).title.string 35 | del response 36 | {%- if cookiecutter.command_line_interface|lower == 'click' %} 37 | 38 | 39 | def test_command_line_interface(): 40 | """Test the CLI.""" 41 | runner = CliRunner() 42 | result = runner.invoke(cli.main) 43 | assert result.exit_code == 0 44 | assert '{{ cookiecutter.project_slug }}.cli.main' in result.output 45 | help_result = runner.invoke(cli.main, ['--help']) 46 | assert help_result.exit_code == 0 47 | assert '--help Show this message and exit.' in help_result.output 48 | {%- endif %} 49 | {%- else %} 50 | 51 | 52 | class Test{{ cookiecutter.project_slug|title }}(unittest.TestCase): 53 | """Tests for `{{ cookiecutter.project_slug }}` package.""" 54 | 55 | def setUp(self): 56 | """Set up test fixtures, if any.""" 57 | 58 | def tearDown(self): 59 | """Tear down test fixtures, if any.""" 60 | 61 | def test_000_something(self): 62 | """Test something.""" 63 | {%- if cookiecutter.command_line_interface|lower == 'click' %} 64 | 65 | def test_command_line_interface(self): 66 | """Test the CLI.""" 67 | runner = CliRunner() 68 | result = runner.invoke(cli.main) 69 | assert result.exit_code == 0 70 | assert '{{ cookiecutter.project_slug }}.cli.main' in result.output 71 | help_result = runner.invoke(cli.main, ['--help']) 72 | assert help_result.exit_code == 0 73 | assert '--help Show this message and exit.' in help_result.output 74 | {%- endif %} 75 | {%- endif %} 76 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | push: 7 | branches: 8 | - master 9 | jobs: 10 | test: 11 | strategy: 12 | matrix: 13 | python-version: ["3.10"] 14 | os: [ubuntu-latest] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - name: Check out repository 18 | uses: actions/checkout@v3 19 | - name: "Set up python ${{ matrix.python-version }}" 20 | id: setup-python 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | #---------------------------------------------- 25 | # ----- install & configure poetry ----- 26 | #---------------------------------------------- 27 | - name: Install Poetry 28 | uses: snok/install-poetry@v1 29 | with: 30 | virtualenvs-create: true 31 | virtualenvs-in-project: true 32 | #---------------------------------------------- 33 | # load cached venv if cache exists 34 | #---------------------------------------------- 35 | - name: Load cached venv 36 | id: cached-poetry-dependencies 37 | uses: actions/cache@v3 38 | with: 39 | path: .venv 40 | key: venv-${{ matrix.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} 41 | #---------------------------------------------- 42 | # install dependencies if cache does not exist 43 | #---------------------------------------------- 44 | - name: Install dependencies 45 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' 46 | run: poetry install --no-interaction --no-root 47 | #---------------------------------------------- 48 | # install your root project, if required 49 | #---------------------------------------------- 50 | - name: Install library 51 | run: poetry install --no-interaction 52 | #---------------------------------------------- 53 | # Run tests 54 | #---------------------------------------------- 55 | - name: Run tests 56 | run: poetry run invoke test 57 | #---------------------------------------------- 58 | # Build docs 59 | #---------------------------------------------- 60 | - name: Build docs 61 | run: poetry run invoke docs 62 | #---------------------------------------------- 63 | # Deploy docs (if on master) 64 | #---------------------------------------------- 65 | - name: Deploy docs 🚀 66 | if: github.ref == 'refs/heads/master' 67 | uses: JamesIves/github-pages-deploy-action@v4 68 | with: 69 | folder: docs/_build -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Cookiecutter PyPackage 3 | ====================== 4 | 5 | .. image:: https://travis-ci.org/briggySmalls/cookiecutter-pypackage.svg?branch=master 6 | :target: https://travis-ci.org/briggySmalls/cookiecutter-pypackage 7 | :alt: Linux build status on Travis CI 8 | 9 | .. image:: https://ci.appveyor.com/api/projects/status/github/briggySmalls/cookiecutter-pypackage?branch=master&svg=true 10 | :target: https://ci.appveyor.com/project/briggySmalls/cookiecutter-pypackage/branch/master 11 | :alt: Windows build status on Appveyor 12 | 13 | Cookiecutter_ template for a Python package, forked from `audreyr/cookiecutter-pypackage`_. 14 | 15 | * GitHub repo: https://github.com/briggySmalls/cookiecutter-pypackage/ 16 | * Documentation: https://briggysmalls.github.io/cookiecutter-pypackage/ 17 | * Free software: BSD license 18 | 19 | .. _audreyr/cookiecutter-pypackage: https://github.com/audreyr/cookiecutter-pypackage 20 | .. _Cookiecutter: https://github.com/audreyr/cookiecutter 21 | 22 | Features 23 | -------- 24 | 25 | This template has all of the features of the original `audreyr/cookiecutter-pypackage`_, plus the following: 26 | 27 | * Dependency tracking using poetry_ 28 | * Linting provided by both pylint_ and flake8_ [executed by Tox] 29 | * Formatting provided by yapf_ and isort_ [checked by Tox] 30 | * Autodoc your code from Google docstring style (optional) 31 | * All development tasks (lint, format, test, etc) wrapped up in a python CLI by invoke_ 32 | 33 | Quickstart 34 | ---------- 35 | 36 | Install the latest Cookiecutter if you haven't installed it yet (this requires 37 | Cookiecutter 1.4.0 or higher):: 38 | 39 | pip install -U cookiecutter 40 | 41 | Generate a Python package project:: 42 | 43 | cookiecutter https://github.com/briggySmalls/cookiecutter-pypackage.git 44 | 45 | Then: 46 | 47 | * Create a repo and put it there. 48 | * Add the repo to your Travis-CI_ account. 49 | * Install the dev requirements into a virtualenv. (``poetry install``) 50 | * Run the Travis CLI command `travis encrypt --add deploy.password` to encrypt your PyPI password in Travis config 51 | and activate automated deployment on PyPI when you push a new tag to master branch. 52 | * Add the repo to your `Read the Docs`_ account + turn on the Read the Docs service hook. 53 | * Release your package by pushing a new tag to master. 54 | * Get your code on! 😎 Add your package dependencies as you go, locking them into your virtual environment with ``poetry add``. 55 | * Activate your project on `pyup.io`_. 56 | 57 | .. _`pip docs for requirements files`: https://pip.pypa.io/en/stable/user_guide/#requirements-files 58 | .. _Register: https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives 59 | 60 | For more details, see the `cookiecutter-pypackage tutorial`_. 61 | 62 | .. _`cookiecutter-pypackage tutorial`: https://briggysmalls.github.io/cookiecutter-pypackage/tutorial.html 63 | 64 | .. _invoke: http://www.pyinvoke.org/ 65 | .. _isort: https://pypi.org/project/isort/ 66 | .. _yapf: https://github.com/google/yapf 67 | .. _flake8: https://pypi.org/project/flake8/ 68 | .. _pylint: https://www.pylint.org/ 69 | .. _poetry: https://python-poetry.org/ 70 | .. _original_pypackage: https://github.com/briggySmalls/cookiecutter-pypackage/ 71 | .. _Travis-CI: http://travis-ci.org/ 72 | .. _Tox: http://testrun.org/tox/ 73 | .. _Sphinx: http://sphinx-doc.org/ 74 | .. _Read the Docs: https://readthedocs.io/ 75 | .. _`pyup.io`: https://pyup.io/ 76 | .. _bump2version: https://github.com/c4urself/bump2version 77 | .. _Punch: https://github.com/lgiordani/punch 78 | .. _PyPi: https://pypi.python.org/pypi 79 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============ 4 | Contributing 5 | ============ 6 | 7 | Contributions are welcome, and they are greatly appreciated! Every little bit 8 | helps, and credit will always be given. 9 | 10 | You can contribute in many ways: 11 | 12 | Types of Contributions 13 | ---------------------- 14 | 15 | Report Bugs 16 | ~~~~~~~~~~~ 17 | 18 | Report bugs at https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/issues. 19 | 20 | If you are reporting a bug, please include: 21 | 22 | * Your operating system name and version. 23 | * Any details about your local setup that might be helpful in troubleshooting. 24 | * Detailed steps to reproduce the bug. 25 | 26 | Fix Bugs 27 | ~~~~~~~~ 28 | 29 | Look through the GitHub issues for bugs. Anything tagged with "bug" and "help 30 | wanted" is open to whoever wants to implement it. 31 | 32 | Implement Features 33 | ~~~~~~~~~~~~~~~~~~ 34 | 35 | Look through the GitHub issues for features. Anything tagged with "enhancement" 36 | and "help wanted" is open to whoever wants to implement it. 37 | 38 | Write Documentation 39 | ~~~~~~~~~~~~~~~~~~~ 40 | 41 | {{ cookiecutter.project_name }} could always use more documentation, whether as part of the 42 | official {{ cookiecutter.project_name }} docs, in docstrings, or even on the web in blog posts, 43 | articles, and such. 44 | 45 | Submit Feedback 46 | ~~~~~~~~~~~~~~~ 47 | 48 | The best way to send feedback is to file an issue at https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/issues. 49 | 50 | If you are proposing a feature: 51 | 52 | * Explain in detail how it would work. 53 | * Keep the scope as narrow as possible, to make it easier to implement. 54 | * Remember that this is a volunteer-driven project, and that contributions 55 | are welcome :) 56 | 57 | Get Started! 58 | ------------ 59 | 60 | Ready to contribute? Here's how to set up `{{ cookiecutter.project_slug }}` for local development. 61 | 62 | #. Fork the `{{ cookiecutter.project_slug }}` repo on GitHub. 63 | #. Clone your fork locally:: 64 | 65 | $ git clone git@github.com:your_name_here/{{ cookiecutter.project_slug }}.git 66 | 67 | #. Ensure `poetry is installed`_. 68 | #. Install dependencies and start your virtualenv:: 69 | 70 | $ poetry install 71 | 72 | #. Create a branch for local development:: 73 | 74 | $ git checkout -b name-of-your-bugfix-or-feature 75 | 76 | Now you can make your changes locally. 77 | 78 | #. When you're done making changes, check that your changes pass the 79 | tests, including testing other Python versions, with tox:: 80 | 81 | $ tox 82 | 83 | #. Commit your changes and push your branch to GitHub:: 84 | 85 | $ git add . 86 | $ git commit -m "Your detailed description of your changes." 87 | $ git push origin name-of-your-bugfix-or-feature 88 | 89 | #. Submit a pull request through the GitHub website. 90 | 91 | .. _poetry is installed: https://python-poetry.org/docs/ 92 | 93 | Pull Request Guidelines 94 | ----------------------- 95 | 96 | Before you submit a pull request, check that it meets these guidelines: 97 | 98 | 1. The pull request should include tests. 99 | 2. If the pull request adds functionality, the docs should be updated. Put 100 | your new functionality into a function with a docstring, and add the 101 | feature to the list in README.rst. 102 | 3. The pull request should work for Python 3.5, 3.6, 3.7 and 3.8, and for PyPy. Check 103 | https://travis-ci.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/pull_requests 104 | and make sure that the tests pass for all supported Python versions. 105 | 106 | Tips 107 | ---- 108 | 109 | To run a subset of tests:: 110 | 111 | {% if cookiecutter.use_pytest == 'y' -%} 112 | $ pytest tests.test_{{ cookiecutter.project_slug }} 113 | {% else %} 114 | $ python -m unittest tests.test_{{ cookiecutter.project_slug }} 115 | {%- endif %} 116 | 117 | Deploying 118 | --------- 119 | 120 | A reminder for the maintainers on how to deploy. 121 | Make sure all your changes are committed (including an entry in HISTORY.rst). 122 | Then run:: 123 | 124 | $ bump2version patch # possible: major / minor / patch 125 | $ git push 126 | $ git push --tags 127 | 128 | Travis will then deploy to PyPI if tests pass. 129 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/tasks.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tasks for maintaining the project. 3 | 4 | Execute 'invoke --list' for guidance on using Invoke 5 | """ 6 | import shutil 7 | import platform 8 | 9 | from invoke import task 10 | from pathlib import Path 11 | import webbrowser 12 | 13 | 14 | ROOT_DIR = Path(__file__).parent 15 | SETUP_FILE = ROOT_DIR.joinpath("setup.py") 16 | TEST_DIR = ROOT_DIR.joinpath("tests") 17 | SOURCE_DIR = ROOT_DIR.joinpath("{{ cookiecutter.project_slug }}") 18 | TOX_DIR = ROOT_DIR.joinpath(".tox") 19 | COVERAGE_FILE = ROOT_DIR.joinpath(".coverage") 20 | COVERAGE_DIR = ROOT_DIR.joinpath("htmlcov") 21 | COVERAGE_REPORT = COVERAGE_DIR.joinpath("index.html") 22 | DOCS_DIR = ROOT_DIR.joinpath("docs") 23 | DOCS_BUILD_DIR = DOCS_DIR.joinpath("_build") 24 | DOCS_INDEX = DOCS_BUILD_DIR.joinpath("index.html") 25 | PYTHON_DIRS = [str(d) for d in [SOURCE_DIR, TEST_DIR]] 26 | 27 | 28 | def _delete_file(file): 29 | try: 30 | file.unlink(missing_ok=True) 31 | except TypeError: 32 | # missing_ok argument added in 3.8 33 | try: 34 | file.unlink() 35 | except FileNotFoundError: 36 | pass 37 | 38 | 39 | def _run(c, command): 40 | return c.run(command, pty=platform.system() != 'Windows') 41 | 42 | 43 | @task(help={'check': "Checks if source is formatted without applying changes"}) 44 | def format(c, check=False): 45 | """ 46 | Format code 47 | """ 48 | python_dirs_string = " ".join(PYTHON_DIRS) 49 | # Run yapf 50 | yapf_options = '--diff' if check else '' 51 | _run(c, "black {} {}".format(yapf_options, python_dirs_string)) 52 | # Run isort 53 | isort_options = '--recursive {}'.format( 54 | '--check-only --diff' if check else '') 55 | _run(c, "isort {} {}".format(isort_options, python_dirs_string)) 56 | 57 | 58 | @task 59 | def lint_flake8(c): 60 | """ 61 | Lint code with flake8 62 | """ 63 | _run(c, "flake8 {}".format(" ".join(PYTHON_DIRS))) 64 | 65 | 66 | @task 67 | def lint_pylint(c): 68 | """ 69 | Lint code with pylint 70 | """ 71 | _run(c, "pylint {}".format(" ".join(PYTHON_DIRS))) 72 | 73 | 74 | @task(lint_flake8, lint_pylint) 75 | def lint(c): 76 | """ 77 | Run all linting 78 | """ 79 | 80 | 81 | @task 82 | def test(c): 83 | """ 84 | Run tests 85 | """ 86 | _run(c, "pytest") 87 | 88 | 89 | @task(help={'publish': "Publish the result via coveralls"}) 90 | def coverage(c, publish=False): 91 | """ 92 | Create coverage report 93 | """ 94 | _run(c, "coverage run --source {} -m pytest".format(SOURCE_DIR)) 95 | _run(c, "coverage report") 96 | if publish: 97 | # Publish the results via coveralls 98 | _run(c, "coveralls") 99 | else: 100 | # Build a local report 101 | _run(c, "coverage html") 102 | webbrowser.open(COVERAGE_REPORT.as_uri()) 103 | 104 | 105 | @task(help={'launch': "Launch documentation in the web browser"}) 106 | def docs(c, launch=True): 107 | """ 108 | Generate documentation 109 | """ 110 | _run(c, "sphinx-build -b html {} {}".format(DOCS_DIR, DOCS_BUILD_DIR)) 111 | if launch: 112 | webbrowser.open(DOCS_INDEX.as_uri()) 113 | 114 | 115 | @task 116 | def clean_docs(c): 117 | """ 118 | Clean up files from documentation builds 119 | """ 120 | _run(c, "rm -fr {}".format(DOCS_BUILD_DIR)) 121 | 122 | 123 | @task 124 | def clean_build(c): 125 | """ 126 | Clean up files from package building 127 | """ 128 | _run(c, "rm -fr build/") 129 | _run(c, "rm -fr dist/") 130 | _run(c, "rm -fr .eggs/") 131 | _run(c, "find . -name '*.egg-info' -exec rm -fr {} +") 132 | _run(c, "find . -name '*.egg' -exec rm -f {} +") 133 | 134 | 135 | @task 136 | def clean_python(c): 137 | """ 138 | Clean up python file artifacts 139 | """ 140 | _run(c, "find . -name '*.pyc' -exec rm -f {} +") 141 | _run(c, "find . -name '*.pyo' -exec rm -f {} +") 142 | _run(c, "find . -name '*~' -exec rm -f {} +") 143 | _run(c, "find . -name '__pycache__' -exec rm -fr {} +") 144 | 145 | 146 | @task 147 | def clean_tests(c): 148 | """ 149 | Clean up files from testing 150 | """ 151 | _delete_file(COVERAGE_FILE) 152 | shutil.rmtree(TOX_DIR, ignore_errors=True) 153 | shutil.rmtree(COVERAGE_DIR, ignore_errors=True) 154 | 155 | 156 | @task(pre=[clean_build, clean_python, clean_tests, clean_docs]) 157 | def clean(c): 158 | """ 159 | Runs all clean sub-tasks 160 | """ 161 | pass 162 | 163 | 164 | @task(clean) 165 | def dist(c): 166 | """ 167 | Build source and wheel packages 168 | """ 169 | _run(c, "poetry build") 170 | 171 | 172 | @task(pre=[clean, dist]) 173 | def release(c): 174 | """ 175 | Make a release of the python package to pypi 176 | """ 177 | _run(c, "poetry publish") 178 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # {{ cookiecutter.project_slug }} documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Jun 9 13:47:02 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another 16 | # directory, add these directories to sys.path here. If the directory is 17 | # relative to the documentation root, use os.path.abspath to make it 18 | # absolute, like shown here. 19 | # 20 | import os 21 | import sys 22 | sys.path.insert(0, os.path.abspath('..')) 23 | 24 | import {{ cookiecutter.project_slug }} 25 | 26 | # -- General configuration --------------------------------------------- 27 | 28 | # If your documentation needs a minimal Sphinx version, state it here. 29 | # 30 | # needs_sphinx = '1.0' 31 | 32 | # Add any Sphinx extension module names here, as strings. They can be 33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | 'sphinx.ext.viewcode', 37 | {%- if cookiecutter.use_google_docstrings == 'y' %} 38 | 'sphinx.ext.napoleon', 39 | {%- endif %} 40 | ] 41 | 42 | # Add any paths that contain templates here, relative to this directory. 43 | templates_path = ['_templates'] 44 | 45 | # The suffix(es) of source filenames. 46 | # You can specify multiple suffix as a list of string: 47 | # 48 | # source_suffix = ['.rst', '.md'] 49 | source_suffix = '.rst' 50 | 51 | # The master toctree document. 52 | master_doc = 'index' 53 | 54 | # General information about the project. 55 | project = '{{ cookiecutter.project_name }}' 56 | copyright = "{% now 'local', '%Y' %}, {{ cookiecutter.full_name }}" 57 | author = "{{ cookiecutter.full_name }}" 58 | 59 | # The version info for the project you're documenting, acts as replacement 60 | # for |version| and |release|, also used in various other places throughout 61 | # the built documents. 62 | # 63 | # The short X.Y version. 64 | version = {{ cookiecutter.project_slug }}.__version__ 65 | # The full version, including alpha/beta/rc tags. 66 | release = {{ cookiecutter.project_slug }}.__version__ 67 | 68 | # The language for content autogenerated by Sphinx. Refer to documentation 69 | # for a list of supported languages. 70 | # 71 | # This is also used if you do content translation via gettext catalogs. 72 | # Usually you set "language" from the command line for these cases. 73 | language = None 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | # This patterns also effect to html_static_path and html_extra_path 78 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 79 | 80 | # The name of the Pygments (syntax highlighting) style to use. 81 | pygments_style = 'sphinx' 82 | 83 | # If true, `todo` and `todoList` produce output, else they produce nothing. 84 | todo_include_todos = False 85 | 86 | 87 | # -- Options for HTML output ------------------------------------------- 88 | 89 | # The theme to use for HTML and HTML Help pages. See the documentation for 90 | # a list of builtin themes. 91 | # 92 | html_theme = 'alabaster' 93 | 94 | # Theme options are theme-specific and customize the look and feel of a 95 | # theme further. For a list of options available for each theme, see the 96 | # documentation. 97 | # 98 | # html_theme_options = {} 99 | 100 | # Add any paths that contain custom static files (such as style sheets) here, 101 | # relative to this directory. They are copied after the builtin static files, 102 | # so a file named "default.css" will overwrite the builtin "default.css". 103 | html_static_path = ['_static'] 104 | 105 | 106 | # -- Options for HTMLHelp output --------------------------------------- 107 | 108 | # Output file base name for HTML help builder. 109 | htmlhelp_basename = '{{ cookiecutter.project_slug }}doc' 110 | 111 | 112 | # -- Options for LaTeX output ------------------------------------------ 113 | 114 | latex_elements = { 115 | # The paper size ('letterpaper' or 'a4paper'). 116 | # 117 | # 'papersize': 'letterpaper', 118 | 119 | # The font size ('10pt', '11pt' or '12pt'). 120 | # 121 | # 'pointsize': '10pt', 122 | 123 | # Additional stuff for the LaTeX preamble. 124 | # 125 | # 'preamble': '', 126 | 127 | # Latex figure (float) alignment 128 | # 129 | # 'figure_align': 'htbp', 130 | } 131 | 132 | # Grouping the document tree into LaTeX files. List of tuples 133 | # (source start file, target name, title, author, documentclass 134 | # [howto, manual, or own class]). 135 | latex_documents = [ 136 | (master_doc, '{{ cookiecutter.project_slug }}.tex', 137 | '{{ cookiecutter.project_name }} Documentation', 138 | '{{ cookiecutter.full_name }}', 'manual'), 139 | ] 140 | 141 | 142 | # -- Options for manual page output ------------------------------------ 143 | 144 | # One entry per manual page. List of tuples 145 | # (source start file, name, description, authors, manual section). 146 | man_pages = [ 147 | (master_doc, '{{ cookiecutter.project_slug }}', 148 | '{{ cookiecutter.project_name }} Documentation', 149 | [author], 1) 150 | ] 151 | 152 | 153 | # -- Options for Texinfo output ---------------------------------------- 154 | 155 | # Grouping the document tree into Texinfo files. List of tuples 156 | # (source start file, target name, title, author, 157 | # dir menu entry, description, category) 158 | texinfo_documents = [ 159 | (master_doc, '{{ cookiecutter.project_slug }}', 160 | '{{ cookiecutter.project_name }} Documentation', 161 | author, 162 | '{{ cookiecutter.project_slug }}', 163 | 'One line description of project.', 164 | 'Miscellaneous'), 165 | ] 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/LICENSE: -------------------------------------------------------------------------------- 1 | {% if cookiecutter.open_source_license == 'MIT' -%} 2 | MIT License 3 | 4 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | {% elif cookiecutter.open_source_license == 'BSD-3-Clause' %} 24 | 25 | BSD License 26 | 27 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 28 | All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or without modification, 31 | are permitted provided that the following conditions are met: 32 | 33 | * Redistributions of source code must retain the above copyright notice, this 34 | list of conditions and the following disclaimer. 35 | 36 | * Redistributions in binary form must reproduce the above copyright notice, this 37 | list of conditions and the following disclaimer in the documentation and/or 38 | other materials provided with the distribution. 39 | 40 | * Neither the name of the copyright holder nor the names of its 41 | contributors may be used to endorse or promote products derived from this 42 | software without specific prior written permission. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 45 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 46 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 47 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 48 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 49 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 50 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 51 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 52 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 53 | OF THE POSSIBILITY OF SUCH DAMAGE. 54 | {% elif cookiecutter.open_source_license == 'ISC' -%} 55 | ISC License 56 | 57 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 58 | 59 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 62 | {% elif cookiecutter.open_source_license == 'Apache-2.0' -%} 63 | Apache Software License 2.0 64 | 65 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 66 | 67 | Licensed under the Apache License, Version 2.0 (the "License"); 68 | you may not use this file except in compliance with the License. 69 | You may obtain a copy of the License at 70 | 71 | http://www.apache.org/licenses/LICENSE-2.0 72 | 73 | Unless required by applicable law or agreed to in writing, software 74 | distributed under the License is distributed on an "AS IS" BASIS, 75 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 76 | See the License for the specific language governing permissions and 77 | limitations under the License. 78 | {% elif cookiecutter.open_source_license == 'GPL-3.0-only' -%} 79 | GNU GENERAL PUBLIC LICENSE 80 | Version 3, 29 June 2007 81 | 82 | {{ cookiecutter.project_short_description }} 83 | Copyright (C) {% now 'local', '%Y' %} {{ cookiecutter.full_name }} 84 | 85 | This program is free software: you can redistribute it and/or modify 86 | it under the terms of the GNU General Public License as published by 87 | the Free Software Foundation, either version 3 of the License, or 88 | (at your option) any later version. 89 | 90 | This program is distributed in the hope that it will be useful, 91 | but WITHOUT ANY WARRANTY; without even the implied warranty of 92 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 93 | GNU General Public License for more details. 94 | 95 | You should have received a copy of the GNU General Public License 96 | along with this program. If not, see . 97 | 98 | Also add information on how to contact you by electronic and paper mail. 99 | 100 | You should also get your employer (if you work as a programmer) or school, 101 | if any, to sign a "copyright disclaimer" for the program, if necessary. 102 | For more information on this, and how to apply and follow the GNU GPL, see 103 | . 104 | 105 | The GNU General Public License does not permit incorporating your program 106 | into proprietary programs. If your program is a subroutine library, you 107 | may consider it more useful to permit linking proprietary applications with 108 | the library. If this is what you want to do, use the GNU Lesser General 109 | Public License instead of this License. But first, please read 110 | . 111 | {% endif %} -------------------------------------------------------------------------------- /docs/tutorial.rst: -------------------------------------------------------------------------------- 1 | Tutorial 2 | ======== 3 | 4 | .. note:: Did you find any of these instructions confusing? `Edit this file`_ 5 | and submit a pull request with your improvements! 6 | 7 | .. _`Edit this file`: https://github.com/audreyr/cookiecutter-pypackage/blob/master/docs/tutorial.rst 8 | 9 | To start with, you will need a `GitHub account`_ and an account on `PyPI`_. Create these before you get started on this tutorial. If you are new to Git and GitHub, you should probably spend a few minutes on some of the tutorials at the top of the page at `GitHub Help`_. 10 | 11 | .. _`GitHub account`: https://github.com/ 12 | .. _`PyPI`: https://pypi.python.org/pypi 13 | .. _`GitHub Help`: https://help.github.com/ 14 | 15 | 16 | Step 1: Install Cookiecutter 17 | ---------------------------- 18 | 19 | Install cookiecutter: 20 | 21 | .. code-block:: bash 22 | 23 | pip install cookiecutter 24 | 25 | We'll also need poetry so [install that too](https://python-poetry.org/docs/#installation): 26 | 27 | Step 2: Generate Your Package 28 | ----------------------------- 29 | 30 | Now it's time to generate your Python package. 31 | 32 | Use cookiecutter, pointing it at the cookiecutter-pypackage repo: 33 | 34 | .. code-block:: bash 35 | 36 | cookiecutter https://github.com/briggySmalls/cookiecutter-pypackage.git 37 | 38 | You'll be asked to enter a bunch of values to set the package up. 39 | If you don't know what to enter, stick with the defaults. 40 | 41 | 42 | Step 3: Create a GitHub Repo 43 | ---------------------------- 44 | 45 | Go to your GitHub account and create a new repo named ``mypackage``, where ``mypackage`` matches the ``[project_slug]`` from your answers to running cookiecutter. This is so that Travis CI and pyup.io can find it when we get to Step 5. 46 | 47 | You will find one folder named after the ``[project_slug]``. Move into this folder, and then setup git to use your GitHub repo and upload the code: 48 | 49 | .. code-block:: bash 50 | 51 | cd mypackage 52 | git init . 53 | git add . 54 | git commit -m "Initial skeleton." 55 | git remote add origin git@github.com:myusername/mypackage.git 56 | git push -u origin master 57 | 58 | Where ``myusername`` and ``mypackage`` are adjusted for your username and package name. 59 | 60 | You'll need a ssh key to push the repo. You can `Generate`_ a key or `Add`_ an existing one. 61 | 62 | .. _`Generate`: https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/ 63 | .. _`Add`: https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/ 64 | 65 | Step 4: Install Dev Requirements 66 | -------------------------------- 67 | 68 | You should still be in the folder containing the ``pyproject.toml`` file. 69 | 70 | Install the new project's local development requirements inside a virtual environment using pipenv: 71 | 72 | .. code-block:: bash 73 | 74 | poetry install 75 | 76 | 77 | Step 5: Set Up Travis CI 78 | ------------------------ 79 | 80 | `Travis CI org`_ [*]_ is a continuous integration tool used to prevent integration problems. Every commit to the master branch will trigger automated builds of the application. 81 | 82 | Login using your Github credentials. It may take a few minutes for Travis CI to load up a list of all your GitHub repos. They will be listed with boxes to the left of the repo name, where the boxes have an ``X`` in them, meaning it is not connected to Travis CI. 83 | 84 | Add the public repo to your Travis CI account by clicking the ``X`` to switch it "on" in the box next to the ``mypackage`` repo. Do not try to follow the other instructions, that will be taken care of next. 85 | 86 | In your terminal, your virtualenv should still be activated. If it isn't, activate it now. Run the Travis CLI tool to do your Travis CI setup: 87 | 88 | .. code-block:: bash 89 | 90 | travis encrypt --add deploy.password 91 | 92 | This will: 93 | 94 | * Encrypt your PyPI password in your Travis config. 95 | * Activate automated deployment on PyPI when you push a new tag to master branch. 96 | 97 | See :ref:`travis-pypi-setup` for more information. 98 | 99 | .. [*] For private projects go to `Travis CI com`_ 100 | 101 | .. _`Travis CI org`: https://travis-ci.org/ 102 | .. _`Travis CI com`: https://travis-ci.com/ 103 | 104 | 105 | Step 6: Set Up Read the Docs 106 | ---------------------------- 107 | 108 | `Read the Docs`_ hosts documentation for the open source community. Think of it as Continuous Documentation. 109 | 110 | Log into your account at `Read the Docs`_ . If you don't have one, create one and log into it. 111 | 112 | If you are not at your dashboard, choose the pull-down next to your username in the upper right, and select "My Projects". Choose the button to Import the repository and follow the directions. 113 | 114 | Now your documentation will get rebuilt when you make documentation changes to your package. 115 | 116 | .. _`Read the Docs`: https://readthedocs.org/ 117 | 118 | Step 7: Set Up pyup.io 119 | ---------------------- 120 | 121 | `pyup.io`_ is a service that helps you to keep your requirements files up to date. It sends you automated 122 | pull requests whenever there's a new release for one of your dependencies. 123 | 124 | To use it, create a new account at `pyup.io`_ or log into your existing account. 125 | 126 | Click on the green ``Add Repo`` button in the top left corner and select the repo you created in Step 3. A popup will 127 | ask you whether you want to pin your dependencies. Click on ``Pin`` to add the repo. 128 | 129 | Once your repo is set up correctly, the pyup.io badge will show your current update status. 130 | 131 | .. _`pyup.io`: https://pyup.io/ 132 | 133 | Step 8: Release on PyPI 134 | ----------------------- 135 | 136 | The Python Package Index or `PyPI`_ is the official third-party software repository for the Python programming language. Python developers intend it to be a comprehensive catalog of all open source Python packages. 137 | 138 | When you are ready, release your package the standard Python way. 139 | 140 | See `PyPI Help`_ for more information about submitting a package. 141 | 142 | Here's a release checklist you can use: https://gist.github.com/audreyr/5990987 143 | 144 | .. _`PyPI`: https://pypi.python.org/pypi 145 | .. _`PyPI Help`: http://peterdowns.com/posts/first-time-with-pypi.html 146 | 147 | 148 | Having problems? 149 | ---------------- 150 | 151 | Visit our :ref:`troubleshooting` page for help. If that doesn't help, go to our `Issues`_ page and create a new Issue. Be sure to give as much information as possible. 152 | 153 | .. _`Issues`: https://github.com/audreyr/cookiecutter-pypackage/issues 154 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | 5 | Contributions are welcome, and they are greatly appreciated! Every little bit 6 | helps, and credit will always be given. 7 | 8 | You can contribute in many ways: 9 | 10 | Types of Contributions 11 | ---------------------- 12 | 13 | Report Bugs 14 | ~~~~~~~~~~~ 15 | 16 | Report bugs at https://github.com/audreyr/cookiecutter-pypackage/issues 17 | 18 | If you are reporting a bug, please include: 19 | 20 | * Your operating system name and version. 21 | * Any details about your local setup that might be helpful in troubleshooting. 22 | * Detailed steps to reproduce the bug. 23 | 24 | Fix Bugs 25 | ~~~~~~~~ 26 | 27 | Look through the GitHub issues for bugs. Anything tagged with "bug" 28 | and "help wanted" is open to whoever wants to implement a fix for it. 29 | 30 | Implement Features 31 | ~~~~~~~~~~~~~~~~~~ 32 | 33 | Look through the GitHub issues for features. Anything tagged with "enhancement" 34 | and "help wanted" is open to whoever wants to implement it. 35 | 36 | Write Documentation 37 | ~~~~~~~~~~~~~~~~~~~ 38 | 39 | Cookiecutter PyPackage could always use more documentation, whether as part of 40 | the official docs, in docstrings, or even on the web in blog posts, articles, 41 | and such. 42 | 43 | Submit Feedback 44 | ~~~~~~~~~~~~~~~ 45 | 46 | The best way to send feedback is to file an issue at 47 | https://github.com/audreyr/cookiecutter-pypackage/issues. 48 | 49 | If you are proposing a new feature: 50 | 51 | * Explain in detail how it would work. 52 | * Keep the scope as narrow as possible, to make it easier to implement. 53 | * Remember that this is a volunteer-driven project, and that contributions 54 | are welcome :) 55 | 56 | Get Started! 57 | ------------ 58 | 59 | Ready to contribute? Here's how to set up `cookiecutter-pypackage` for local 60 | development. Please note this documentation assumes you already have 61 | `virtualenv` and `Git` installed and ready to go. 62 | 63 | 1. Fork the `cookiecutter-pypackage` repo on GitHub. 64 | 65 | :: 66 | 67 | 2. Clone your fork locally: 68 | 69 | .. code-block:: bash 70 | 71 | $ cd path_for_the_repo 72 | $ git clone git@github.com:YOUR_NAME/cookiecutter-pypackage.git 73 | 74 | :: 75 | 76 | 3. Assuming you have virtualenv installed (If you have Python3.5 this should 77 | already be there), you can create a new environment for your local 78 | development by typing: 79 | 80 | .. code-block:: bash 81 | 82 | $ virtualenv cookiecutter-pypackage-env 83 | $ source cookiecutter-pypackage-env/bin/activate 84 | 85 | This should change the shell to look something like: 86 | 87 | .. code-block:: bash 88 | 89 | (cookiecutter-pypackage-env) $ 90 | 91 | :: 92 | 93 | 4. Create a branch for local development: 94 | 95 | .. code-block:: bash 96 | 97 | $ git checkout -b name-of-your-bugfix-or-feature 98 | 99 | Now you can make your changes locally. 100 | 101 | :: 102 | 103 | 5. When you're done making changes, check that your changes pass flake8. Since, 104 | this package contains mostly templates the flake should be run for tests 105 | directory: 106 | 107 | .. code-block:: bash 108 | 109 | $ flake8 ./tests 110 | 111 | :: 112 | 113 | 6. The next step would be to run the test cases. `cookiecutter-pypackage` uses 114 | pytest, you can run PyTest. Before you run pytest you should ensure all 115 | dependancies are installed: 116 | 117 | .. code-block:: bash 118 | 119 | $ pip install -rrequirements_dev.txt 120 | $ pytest ./tests 121 | 122 | If you get any errors while installing cryptography package (something like 123 | `#include `). Please update your pip version and try again: 124 | 125 | .. code-block:: bash 126 | 127 | # Update pip 128 | $ pip install -U pip 129 | 130 | :: 131 | 132 | 7. Before raising a pull request you should also run tox. This will run the 133 | tests across different versions of Python: 134 | 135 | .. code-block:: bash 136 | 137 | $ tox 138 | 139 | If you are missing flake8, pytest and/or tox, just `pip install` them into 140 | your virtualenv. 141 | 142 | :: 143 | 144 | 8. If your contribution is a bug fix or new feature, you may want to add a test 145 | to the existing test suite. See section Add a New Test below for details. 146 | 147 | :: 148 | 149 | 9. Commit your changes and push your branch to GitHub: 150 | 151 | .. code-block:: bash 152 | 153 | $ git add . 154 | $ git commit -m "Your detailed description of your changes." 155 | $ git push origin name-of-your-bugfix-or-feature 156 | 157 | :: 158 | 159 | 10. Submit a pull request through the GitHub website. 160 | 161 | :: 162 | 163 | Pull Request Guidelines 164 | ----------------------- 165 | 166 | Before you submit a pull request, check that it meets these guidelines: 167 | 168 | 1. The pull request should include tests. 169 | 170 | 2. If the pull request adds functionality, the docs should be updated. Put your 171 | new functionality into a function with a docstring, and add the feature to 172 | the list in README.rst. 173 | 174 | 3. The pull request should work for Python 3.5, 3.6 and 3.7, 3.8 and for PyPy. Check 175 | https://travis-ci.org/audreyr/cookiecutter-pypackage/pull_requests and 176 | make sure that the tests pass for all supported Python versions. 177 | 178 | Add a New Test 179 | -------------- 180 | 181 | When fixing a bug or adding features, it's good practice to add a test to 182 | demonstrate your fix or new feature behaves as expected. These tests should 183 | focus on one tiny bit of functionality and prove changes are correct. 184 | 185 | To write and run your new test, follow these steps: 186 | 187 | 1. Add the new test to `tests/test_bake_project.py`. Focus your test on the 188 | specific bug or a small part of the new feature. 189 | 190 | :: 191 | 192 | 2. If you have already made changes to the code, stash your changes and confirm 193 | all your changes were stashed: 194 | 195 | .. code-block:: bash 196 | 197 | $ git stash 198 | $ git stash list 199 | 200 | :: 201 | 202 | 3. Run your test and confirm that your test fails. If your test does not fail, 203 | rewrite the test until it fails on the original code: 204 | 205 | .. code-block:: bash 206 | 207 | $ pytest ./tests 208 | 209 | :: 210 | 211 | 4. (Optional) Run the tests with tox to ensure that the code changes work with 212 | different Python versions: 213 | 214 | .. code-block:: bash 215 | 216 | $ tox 217 | 218 | :: 219 | 220 | 5. Proceed work on your bug fix or new feature or restore your changes. To 221 | restore your stashed changes and confirm their restoration: 222 | 223 | .. code-block:: bash 224 | 225 | $ git stash pop 226 | $ git stash list 227 | 228 | :: 229 | 230 | 6. Rerun your test and confirm that your test passes. If it passes, 231 | congratulations! 232 | 233 | .. cookiecutter: https://github.com/audreyr/cookiecutter-pypackage 234 | .. virtualenv: https://virtualenv.pypa.io/en/stable/installation 235 | .. git: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git 236 | 237 | -------------------------------------------------------------------------------- /tests/test_bake_project.py: -------------------------------------------------------------------------------- 1 | from contextlib import contextmanager 2 | import shlex 3 | import os 4 | import pytest 5 | import sys 6 | import subprocess 7 | import yaml 8 | import datetime 9 | from cookiecutter.utils import rmtree 10 | 11 | from click.testing import CliRunner 12 | 13 | import importlib 14 | 15 | 16 | _DEPENDENCY_FILE = "pyproject.toml" 17 | _INSTALL_DEPS_COMMANDS = [ 18 | "poetry install", 19 | ] 20 | 21 | 22 | def build_commands(commands): 23 | cmds = _INSTALL_DEPS_COMMANDS.copy() 24 | cmds.extend(commands) 25 | return cmds 26 | 27 | 28 | @contextmanager 29 | def inside_dir(dirpath): 30 | """ 31 | Execute code from inside the given directory 32 | :param dirpath: String, path of the directory the command is being run. 33 | """ 34 | old_path = os.getcwd() 35 | try: 36 | os.chdir(dirpath) 37 | yield 38 | finally: 39 | os.chdir(old_path) 40 | 41 | 42 | @contextmanager 43 | def bake_in_temp_dir(cookies, *args, **kwargs): 44 | """ 45 | Delete the temporal directory that is created when executing the tests 46 | :param cookies: pytest_cookies.Cookies, 47 | cookie to be baked and its temporal files will be removed 48 | """ 49 | result = cookies.bake(*args, **kwargs) 50 | assert result.exception is None 51 | yield result 52 | 53 | 54 | def run_inside_dir(commands, dirpath): 55 | """ 56 | Run a command from inside a given directory, returning the exit status 57 | :param commands: Commands that will be executed 58 | :param dirpath: String, path of the directory the command is being run. 59 | """ 60 | with inside_dir(dirpath): 61 | for command in commands: 62 | output = subprocess.check_output(shlex.split(command)) 63 | print(output) 64 | 65 | 66 | def check_output_inside_dir(command, dirpath): 67 | """Run a command from inside a given directory, returning the command output""" 68 | with inside_dir(dirpath): 69 | return subprocess.check_output(shlex.split(command)) 70 | 71 | 72 | def test_year_compute_in_license_file(cookies): 73 | with bake_in_temp_dir(cookies) as result: 74 | license_file_path = result.project.join('LICENSE') 75 | now = datetime.datetime.now() 76 | assert str(now.year) in license_file_path.read() 77 | 78 | 79 | def project_info(result): 80 | """Get toplevel dir, project_slug, and project dir from baked cookies""" 81 | project_path = str(result.project) 82 | project_slug = os.path.split(project_path)[-1] 83 | project_dir = os.path.join(project_path, project_slug) 84 | return project_path, project_slug, project_dir 85 | 86 | 87 | def test_bake_with_defaults(cookies): 88 | with bake_in_temp_dir(cookies) as result: 89 | assert result.project.isdir() 90 | assert result.exit_code == 0 91 | assert result.exception is None 92 | 93 | found_toplevel_files = [f.basename for f in result.project.listdir()] 94 | assert _DEPENDENCY_FILE in found_toplevel_files 95 | assert 'python_boilerplate' in found_toplevel_files 96 | assert 'tests' in found_toplevel_files 97 | 98 | 99 | @pytest.mark.parametrize("extra_context", [ 100 | {}, 101 | {'full_name': 'name "quote" name'}, 102 | {'full_name': "O'connor"} 103 | ]) 104 | def test_bake_and_run_tests(cookies, extra_context): 105 | with bake_in_temp_dir( 106 | cookies, 107 | extra_context=extra_context, 108 | ) as result: 109 | assert result.project.isdir() 110 | # Test pyproject installs pytest 111 | dep_file_path = result.project.join(_DEPENDENCY_FILE) 112 | lines = dep_file_path.readlines() 113 | assert "pytest = \">=7.2.0\"\n" in lines 114 | # Test contents of test file 115 | test_file_path = result.project.join('tests/test_python_boilerplate.py') 116 | lines = test_file_path.readlines() 117 | assert "import pytest" in ''.join(lines) 118 | # Run the tests 119 | commands = build_commands(["poetry run invoke test"]) 120 | run_inside_dir(commands, str(result.project)) 121 | 122 | 123 | def test_bake_without_author_file(cookies): 124 | with bake_in_temp_dir( 125 | cookies, 126 | extra_context={'create_author_file': 'n'} 127 | ) as result: 128 | found_toplevel_files = [f.basename for f in result.project.listdir()] 129 | assert 'AUTHORS.rst' not in found_toplevel_files 130 | doc_files = [f.basename for f in result.project.join('docs').listdir()] 131 | assert 'authors.rst' not in doc_files 132 | 133 | # Assert there are no spaces in the toc tree 134 | docs_index_path = result.project.join('docs/index.rst') 135 | with open(str(docs_index_path)) as index_file: 136 | assert 'contributing\n history' in index_file.read() 137 | 138 | 139 | @pytest.mark.parametrize("license_info", [ 140 | ('MIT', 'MIT '), 141 | ('BSD-3-Clause', 'Redistributions of source code must retain the ' + 142 | 'above copyright notice, this'), 143 | ('ISC', 'ISC License'), 144 | ('Apache-2.0', 'Licensed under the Apache License, Version 2.0'), 145 | ('GPL-3.0-only', 'GNU GENERAL PUBLIC LICENSE'), 146 | ]) 147 | def test_bake_selecting_license(cookies, license_info): 148 | license, target_string = license_info 149 | with bake_in_temp_dir( 150 | cookies, 151 | extra_context={'open_source_license': license} 152 | ) as result: 153 | assert target_string in result.project.join('LICENSE').read() 154 | assert license in result.project.join(_DEPENDENCY_FILE).read() 155 | 156 | 157 | def test_bake_not_open_source(cookies): 158 | with bake_in_temp_dir( 159 | cookies, 160 | extra_context={'open_source_license': 'Not open source'} 161 | ) as result: 162 | found_toplevel_files = [f.basename for f in result.project.listdir()] 163 | assert _DEPENDENCY_FILE in found_toplevel_files 164 | assert 'LICENSE' not in found_toplevel_files 165 | assert 'License' not in result.project.join('README.rst').read() 166 | assert 'license' not in result.project.join(_DEPENDENCY_FILE).read() 167 | 168 | 169 | def test_not_using_pytest(cookies): 170 | with bake_in_temp_dir(cookies, extra_context={'use_pytest': 'n'}) as result: 171 | assert result.project.isdir() 172 | # Test pyproject doesn't install pytest 173 | dep_file_path = result.project.join(_DEPENDENCY_FILE) 174 | lines = dep_file_path.readlines() 175 | assert "pytest = \"*\"\n" not in lines 176 | # Test contents of test file 177 | test_file_path = result.project.join('tests/test_python_boilerplate.py') 178 | lines = test_file_path.readlines() 179 | assert "import unittest" in ''.join(lines) 180 | assert "import pytest" not in ''.join(lines) 181 | 182 | 183 | def test_using_google_docstrings(cookies): 184 | with bake_in_temp_dir(cookies) as result: 185 | assert result.project.isdir() 186 | # Test docs include sphinx extension 187 | docs_conf_file_path = result.project.join('docs/conf.py') 188 | lines = docs_conf_file_path.readlines() 189 | assert "sphinx.ext.napoleon" in ''.join(lines) 190 | 191 | 192 | def test_not_using_google_docstrings(cookies): 193 | with bake_in_temp_dir(cookies, extra_context={'use_google_docstrings': 'n'}) as result: 194 | assert result.project.isdir() 195 | # Test docs do not include sphinx extension 196 | docs_conf_file_path = result.project.join('docs/conf.py') 197 | lines = docs_conf_file_path.readlines() 198 | assert "sphinx.ext.napoleon" not in ''.join(lines) 199 | 200 | 201 | # def test_project_with_hyphen_in_module_name(cookies): 202 | # result = cookies.bake( 203 | # extra_context={'project_name': 'something-with-a-dash'} 204 | # ) 205 | # assert result.project is not None 206 | # project_path = str(result.project) 207 | # 208 | # # when: 209 | # travis_setup_cmd = ('python travis_pypi_setup.py' 210 | # ' --repo audreyr/cookiecutter-pypackage' 211 | # ' --password invalidpass') 212 | # run_inside_dir(travis_setup_cmd, project_path) 213 | # 214 | # # then: 215 | # result_travis_config = yaml.load( 216 | # open(os.path.join(project_path, ".travis.yml")) 217 | # ) 218 | # assert "secure" in result_travis_config["deploy"]["password"],\ 219 | # "missing password config in .travis.yml" 220 | 221 | 222 | @pytest.mark.parametrize("args", [ 223 | ({'command_line_interface': "No command-line interface"}, False), 224 | ({'command_line_interface': 'Click'}, True), 225 | ]) 226 | def test_bake_with_no_console_script(cookies, args): 227 | context, is_present = args 228 | result = cookies.bake(extra_context=context) 229 | project_path, project_slug, project_dir = project_info(result) 230 | found_project_files = os.listdir(project_dir) 231 | assert ("cli.py" in found_project_files) == is_present 232 | 233 | pyproject_path = os.path.join(project_path, _DEPENDENCY_FILE) 234 | with open(pyproject_path, 'r') as pyproject_file: 235 | assert ('[tool.poetry.scripts]' in pyproject_file.read()) == is_present 236 | 237 | 238 | def test_bake_with_console_script_cli(cookies): 239 | context = {'command_line_interface': 'Click'} 240 | result = cookies.bake(extra_context=context) 241 | project_path, project_slug, project_dir = project_info(result) 242 | module_path = os.path.join(project_dir, 'cli.py') 243 | module_name = '.'.join([project_slug, 'cli']) 244 | spec = importlib.util.spec_from_file_location(module_name, module_path) 245 | cli = importlib.util.module_from_spec(spec) 246 | spec.loader.exec_module(cli) 247 | runner = CliRunner() 248 | noarg_result = runner.invoke(cli.main) 249 | assert noarg_result.exit_code == 0 250 | noarg_output = ' '.join([ 251 | 'Replace this message by putting your code into', 252 | project_slug]) 253 | assert noarg_output in noarg_result.output 254 | help_result = runner.invoke(cli.main, ['--help']) 255 | assert help_result.exit_code == 0 256 | assert 'Show this message' in help_result.output 257 | 258 | 259 | @pytest.mark.parametrize("command", [ 260 | "poetry run invoke format --check", 261 | "poetry run invoke lint", 262 | "poetry run invoke docs --no-launch", 263 | ]) 264 | def test_bake_and_run_and_invoke(cookies, command): 265 | """Run the unit tests of a newly-generated project""" 266 | with bake_in_temp_dir(cookies) as result: 267 | assert result.project.isdir() 268 | commands = build_commands([command]) 269 | run_inside_dir(commands, str(result.project)) 270 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # cookiecutter-pypackage documentation build configuration file, created by 4 | # sphinx-quickstart on Sun Dec 13 09:13:01 2015. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | sys.path.insert(0, os.path.abspath('../{{cookiecutter.project_slug}}')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.doctest', 34 | 'sphinx.ext.todo', 35 | 'sphinx.ext.coverage', 36 | 'sphinx.ext.viewcode', 37 | 'sphinx.ext.githubpages', 38 | ] 39 | 40 | # Add any paths that contain templates here, relative to this directory. 41 | templates_path = ['_templates'] 42 | 43 | # The suffix(es) of source filenames. 44 | # You can specify multiple suffix as a list of string: 45 | # source_suffix = ['.rst', '.md'] 46 | source_suffix = '.rst' 47 | 48 | # The encoding of source files. 49 | #source_encoding = 'utf-8-sig' 50 | 51 | # The master toctree document. 52 | master_doc = 'index' 53 | 54 | # General information about the project. 55 | project = 'cookiecutter-pypackage' 56 | copyright = '2019, Sam Briggs' 57 | author = 'Sam Briggs' 58 | 59 | # The version info for the project you're documenting, acts as replacement for 60 | # |version| and |release|, also used in various other places throughout the 61 | # built documents. 62 | # 63 | # The short X.Y version. 64 | version = '0.9.0' 65 | # The full version, including alpha/beta/rc tags. 66 | release = '0.9.0' 67 | 68 | # The language for content autogenerated by Sphinx. Refer to documentation 69 | # for a list of supported languages. 70 | # 71 | # This is also used if you do content translation via gettext catalogs. 72 | # Usually you set "language" from the command line for these cases. 73 | language = None 74 | 75 | # There are two options for replacing |today|: either, you set today to some 76 | # non-false value, then it is used: 77 | #today = '' 78 | # Else, today_fmt is used as the format for a strftime call. 79 | #today_fmt = '%B %d, %Y' 80 | 81 | # List of patterns, relative to source directory, that match files and 82 | # directories to ignore when looking for source files. 83 | exclude_patterns = ['_build'] 84 | 85 | # The reST default role (used for this markup: `text`) to use for all 86 | # documents. 87 | #default_role = None 88 | 89 | # If true, '()' will be appended to :func: etc. cross-reference text. 90 | #add_function_parentheses = True 91 | 92 | # If true, the current module name will be prepended to all description 93 | # unit titles (such as .. function::). 94 | #add_module_names = True 95 | 96 | # If true, sectionauthor and moduleauthor directives will be shown in the 97 | # output. They are ignored by default. 98 | #show_authors = False 99 | 100 | # The name of the Pygments (syntax highlighting) style to use. 101 | pygments_style = 'sphinx' 102 | 103 | # A list of ignored prefixes for module index sorting. 104 | #modindex_common_prefix = [] 105 | 106 | # If true, keep warnings as "system message" paragraphs in the built documents. 107 | #keep_warnings = False 108 | 109 | # If true, `todo` and `todoList` produce output, else they produce nothing. 110 | todo_include_todos = True 111 | 112 | 113 | # -- Options for HTML output ---------------------------------------------- 114 | 115 | # The theme to use for HTML and HTML Help pages. See the documentation for 116 | # a list of builtin themes. 117 | html_theme = 'sphinx_rtd_theme' 118 | 119 | # Theme options are theme-specific and customize the look and feel of a theme 120 | # further. For a list of options available for each theme, see the 121 | # documentation. 122 | #html_theme_options = {} 123 | 124 | # Add any paths that contain custom themes here, relative to this directory. 125 | #html_theme_path = [] 126 | 127 | # The name for this set of Sphinx documents. If None, it defaults to 128 | # " v documentation". 129 | #html_title = None 130 | 131 | # A shorter title for the navigation bar. Default is the same as html_title. 132 | #html_short_title = None 133 | 134 | # The name of an image file (relative to this directory) to place at the top 135 | # of the sidebar. 136 | #html_logo = None 137 | 138 | # The name of an image file (within the static path) to use as favicon of the 139 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 140 | # pixels large. 141 | #html_favicon = None 142 | 143 | # Add any paths that contain custom static files (such as style sheets) here, 144 | # relative to this directory. They are copied after the builtin static files, 145 | # so a file named "default.css" will overwrite the builtin "default.css". 146 | html_static_path = ['_static'] 147 | 148 | # Add any extra paths that contain custom files (such as robots.txt or 149 | # .htaccess) here, relative to this directory. These files are copied 150 | # directly to the root of the documentation. 151 | #html_extra_path = [] 152 | 153 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 154 | # using the given strftime format. 155 | #html_last_updated_fmt = '%b %d, %Y' 156 | 157 | # If true, SmartyPants will be used to convert quotes and dashes to 158 | # typographically correct entities. 159 | #html_use_smartypants = True 160 | 161 | # Custom sidebar templates, maps document names to template names. 162 | #html_sidebars = {} 163 | 164 | # Additional templates that should be rendered to pages, maps page names to 165 | # template names. 166 | #html_additional_pages = {} 167 | 168 | # If false, no module index is generated. 169 | #html_domain_indices = True 170 | 171 | # If false, no index is generated. 172 | #html_use_index = True 173 | 174 | # If true, the index is split into individual pages for each letter. 175 | #html_split_index = False 176 | 177 | # If true, links to the reST sources are added to the pages. 178 | #html_show_sourcelink = True 179 | 180 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 181 | #html_show_sphinx = True 182 | 183 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 184 | #html_show_copyright = True 185 | 186 | # If true, an OpenSearch description file will be output, and all pages will 187 | # contain a tag referring to it. The value of this option must be the 188 | # base URL from which the finished HTML is served. 189 | #html_use_opensearch = '' 190 | 191 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 192 | #html_file_suffix = None 193 | 194 | # Language to be used for generating the HTML full-text search index. 195 | # Sphinx supports the following languages: 196 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 197 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' 198 | #html_search_language = 'en' 199 | 200 | # A dictionary with options for the search language support, empty by default. 201 | # Now only 'ja' uses this config value 202 | #html_search_options = {'type': 'default'} 203 | 204 | # The name of a javascript file (relative to the configuration directory) that 205 | # implements a search results scorer. If empty, the default will be used. 206 | #html_search_scorer = 'scorer.js' 207 | 208 | # Output file base name for HTML help builder. 209 | htmlhelp_basename = 'cookiecutter-pypackagedoc' 210 | 211 | # -- Options for LaTeX output --------------------------------------------- 212 | 213 | latex_elements = { 214 | # The paper size ('letterpaper' or 'a4paper'). 215 | #'papersize': 'letterpaper', 216 | 217 | # The font size ('10pt', '11pt' or '12pt'). 218 | #'pointsize': '10pt', 219 | 220 | # Additional stuff for the LaTeX preamble. 221 | #'preamble': '', 222 | 223 | # Latex figure (float) alignment 224 | #'figure_align': 'htbp', 225 | } 226 | 227 | # Grouping the document tree into LaTeX files. List of tuples 228 | # (source start file, target name, title, 229 | # author, documentclass [howto, manual, or own class]). 230 | latex_documents = [ 231 | (master_doc, 'cookiecutter-pypackage.tex', 'cookiecutter-pypackage Documentation', 232 | 'Audrey Roy Greenfeld', 'manual'), 233 | ] 234 | 235 | # The name of an image file (relative to this directory) to place at the top of 236 | # the title page. 237 | #latex_logo = None 238 | 239 | # For "manual" documents, if this is true, then toplevel headings are parts, 240 | # not chapters. 241 | #latex_use_parts = False 242 | 243 | # If true, show page references after internal links. 244 | #latex_show_pagerefs = False 245 | 246 | # If true, show URL addresses after external links. 247 | #latex_show_urls = False 248 | 249 | # Documents to append as an appendix to all manuals. 250 | #latex_appendices = [] 251 | 252 | # If false, no module index is generated. 253 | #latex_domain_indices = True 254 | 255 | 256 | # -- Options for manual page output --------------------------------------- 257 | 258 | # One entry per manual page. List of tuples 259 | # (source start file, name, description, authors, manual section). 260 | man_pages = [ 261 | (master_doc, 'cookiecutter-pypackage', 'cookiecutter-pypackage Documentation', 262 | [author], 1) 263 | ] 264 | 265 | # If true, show URL addresses after external links. 266 | #man_show_urls = False 267 | 268 | 269 | # -- Options for Texinfo output ------------------------------------------- 270 | 271 | # Grouping the document tree into Texinfo files. List of tuples 272 | # (source start file, target name, title, author, 273 | # dir menu entry, description, category) 274 | texinfo_documents = [ 275 | (master_doc, 'cookiecutter-pypackage', 'cookiecutter-pypackage Documentation', 276 | author, 'cookiecutter-pypackage', 'One line description of project.', 277 | 'Miscellaneous'), 278 | ] 279 | 280 | # Documents to append as an appendix to all manuals. 281 | #texinfo_appendices = [] 282 | 283 | # If false, no module index is generated. 284 | #texinfo_domain_indices = True 285 | 286 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 287 | #texinfo_show_urls = 'footnote' 288 | 289 | # If true, do not generate a @detailmenu in the "Top" node's menu. 290 | #texinfo_no_detailmenu = False 291 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "alabaster" 3 | version = "0.7.12" 4 | description = "A configurable sidebar-enabled Sphinx theme" 5 | category = "dev" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "arrow" 11 | version = "0.13.2" 12 | description = "Better dates and times for Python" 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 16 | 17 | [package.dependencies] 18 | python-dateutil = "*" 19 | 20 | [[package]] 21 | name = "attrs" 22 | version = "19.3.0" 23 | description = "Classes Without Boilerplate" 24 | category = "dev" 25 | optional = false 26 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 27 | 28 | [package.extras] 29 | azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-azurepipelines", "six", "zope.interface"] 30 | dev = ["coverage", "hypothesis", "pre-commit", "pympler", "pytest (>=4.3.0)", "six", "sphinx", "zope.interface"] 31 | docs = ["sphinx", "zope.interface"] 32 | tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] 33 | 34 | [[package]] 35 | name = "babel" 36 | version = "2.10.3" 37 | description = "Internationalization utilities" 38 | category = "dev" 39 | optional = false 40 | python-versions = ">=3.6" 41 | 42 | [package.dependencies] 43 | pytz = ">=2015.7" 44 | 45 | [[package]] 46 | name = "binaryornot" 47 | version = "0.4.4" 48 | description = "Ultra-lightweight pure Python package to check if a file is binary or text." 49 | category = "dev" 50 | optional = false 51 | python-versions = "*" 52 | 53 | [package.dependencies] 54 | chardet = ">=3.0.2" 55 | 56 | [[package]] 57 | name = "blessings" 58 | version = "1.7" 59 | description = "A thin, practical wrapper around terminal coloring, styling, and positioning" 60 | category = "dev" 61 | optional = false 62 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 63 | 64 | [package.dependencies] 65 | six = "*" 66 | 67 | [[package]] 68 | name = "bump2version" 69 | version = "1.0.0" 70 | description = "Version-bump your software with a single command!" 71 | category = "dev" 72 | optional = false 73 | python-versions = ">=3.5" 74 | 75 | [[package]] 76 | name = "certifi" 77 | version = "2020.4.5.1" 78 | description = "Python package for providing Mozilla's CA Bundle." 79 | category = "dev" 80 | optional = false 81 | python-versions = "*" 82 | 83 | [[package]] 84 | name = "chardet" 85 | version = "3.0.4" 86 | description = "Universal encoding detector for Python 2 and 3" 87 | category = "dev" 88 | optional = false 89 | python-versions = "*" 90 | 91 | [[package]] 92 | name = "charset-normalizer" 93 | version = "2.1.1" 94 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 95 | category = "dev" 96 | optional = false 97 | python-versions = ">=3.6.0" 98 | 99 | [package.extras] 100 | unicode-backport = ["unicodedata2"] 101 | 102 | [[package]] 103 | name = "click" 104 | version = "7.1.2" 105 | description = "Composable command line interface toolkit" 106 | category = "dev" 107 | optional = false 108 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 109 | 110 | [[package]] 111 | name = "colorama" 112 | version = "0.4.6" 113 | description = "Cross-platform colored terminal text." 114 | category = "dev" 115 | optional = false 116 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 117 | 118 | [[package]] 119 | name = "cookiecutter" 120 | version = "2.1.1" 121 | description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." 122 | category = "dev" 123 | optional = false 124 | python-versions = ">=3.7" 125 | 126 | [package.dependencies] 127 | binaryornot = ">=0.4.4" 128 | click = ">=7.0,<9.0.0" 129 | Jinja2 = ">=2.7,<4.0.0" 130 | jinja2-time = ">=0.2.0" 131 | python-slugify = ">=4.0.0" 132 | pyyaml = ">=5.3.1" 133 | requests = ">=2.23.0" 134 | 135 | [[package]] 136 | name = "docutils" 137 | version = "0.16" 138 | description = "Docutils -- Python Documentation Utilities" 139 | category = "dev" 140 | optional = false 141 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 142 | 143 | [[package]] 144 | name = "enum34" 145 | version = "1.1.10" 146 | description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" 147 | category = "dev" 148 | optional = false 149 | python-versions = "*" 150 | 151 | [[package]] 152 | name = "exceptiongroup" 153 | version = "1.0.0rc9" 154 | description = "Backport of PEP 654 (exception groups)" 155 | category = "dev" 156 | optional = false 157 | python-versions = ">=3.7" 158 | 159 | [package.extras] 160 | test = ["pytest (>=6)"] 161 | 162 | [[package]] 163 | name = "idna" 164 | version = "2.9" 165 | description = "Internationalized Domain Names in Applications (IDNA)" 166 | category = "dev" 167 | optional = false 168 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 169 | 170 | [[package]] 171 | name = "imagesize" 172 | version = "1.4.1" 173 | description = "Getting image size from png/jpeg/jpeg2000/gif file" 174 | category = "dev" 175 | optional = false 176 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 177 | 178 | [[package]] 179 | name = "importlib-metadata" 180 | version = "5.0.0" 181 | description = "Read metadata from Python packages" 182 | category = "dev" 183 | optional = false 184 | python-versions = ">=3.7" 185 | 186 | [package.dependencies] 187 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 188 | zipp = ">=0.5" 189 | 190 | [package.extras] 191 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] 192 | perf = ["ipython"] 193 | testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] 194 | 195 | [[package]] 196 | name = "iniconfig" 197 | version = "1.1.1" 198 | description = "iniconfig: brain-dead simple config-ini parsing" 199 | category = "dev" 200 | optional = false 201 | python-versions = "*" 202 | 203 | [[package]] 204 | name = "invocations" 205 | version = "1.4.0" 206 | description = "Common/best-practice Invoke tasks and collections" 207 | category = "dev" 208 | optional = false 209 | python-versions = "*" 210 | 211 | [package.dependencies] 212 | blessings = ">=1.6,<2" 213 | enum34 = ">=1.1,<2" 214 | invoke = ">=1.0,<2.0" 215 | releases = ">=1.6,<2" 216 | semantic-version = ">=2.4,<3" 217 | tabulate = "0.7.5" 218 | tqdm = ">=4.8.1" 219 | 220 | [[package]] 221 | name = "invoke" 222 | version = "1.4.1" 223 | description = "Pythonic task execution" 224 | category = "dev" 225 | optional = false 226 | python-versions = "*" 227 | 228 | [[package]] 229 | name = "jinja2" 230 | version = "3.1.2" 231 | description = "A very fast and expressive template engine." 232 | category = "dev" 233 | optional = false 234 | python-versions = ">=3.7" 235 | 236 | [package.dependencies] 237 | MarkupSafe = ">=2.0" 238 | 239 | [package.extras] 240 | i18n = ["Babel (>=2.7)"] 241 | 242 | [[package]] 243 | name = "jinja2-time" 244 | version = "0.2.0" 245 | description = "Jinja2 Extension for Dates and Times" 246 | category = "dev" 247 | optional = false 248 | python-versions = "*" 249 | 250 | [package.dependencies] 251 | arrow = "*" 252 | jinja2 = "*" 253 | 254 | [[package]] 255 | name = "markupsafe" 256 | version = "2.1.1" 257 | description = "Safely add untrusted strings to HTML/XML markup." 258 | category = "dev" 259 | optional = false 260 | python-versions = ">=3.7" 261 | 262 | [[package]] 263 | name = "packaging" 264 | version = "21.3" 265 | description = "Core utilities for Python packages" 266 | category = "dev" 267 | optional = false 268 | python-versions = ">=3.6" 269 | 270 | [package.dependencies] 271 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 272 | 273 | [[package]] 274 | name = "pluggy" 275 | version = "0.13.1" 276 | description = "plugin and hook calling mechanisms for python" 277 | category = "dev" 278 | optional = false 279 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 280 | 281 | [package.dependencies] 282 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 283 | 284 | [package.extras] 285 | dev = ["pre-commit", "tox"] 286 | 287 | [[package]] 288 | name = "pygments" 289 | version = "2.13.0" 290 | description = "Pygments is a syntax highlighting package written in Python." 291 | category = "dev" 292 | optional = false 293 | python-versions = ">=3.6" 294 | 295 | [package.extras] 296 | plugins = ["importlib-metadata"] 297 | 298 | [[package]] 299 | name = "pyparsing" 300 | version = "2.4.7" 301 | description = "Python parsing module" 302 | category = "dev" 303 | optional = false 304 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 305 | 306 | [[package]] 307 | name = "pytest" 308 | version = "7.2.0" 309 | description = "pytest: simple powerful testing with Python" 310 | category = "dev" 311 | optional = false 312 | python-versions = ">=3.7" 313 | 314 | [package.dependencies] 315 | attrs = ">=19.2.0" 316 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 317 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 318 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 319 | iniconfig = "*" 320 | packaging = "*" 321 | pluggy = ">=0.12,<2.0" 322 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 323 | 324 | [package.extras] 325 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 326 | 327 | [[package]] 328 | name = "pytest-cookies" 329 | version = "0.6.1" 330 | description = "The pytest plugin for your Cookiecutter templates. 🍪" 331 | category = "dev" 332 | optional = false 333 | python-versions = ">=3.6" 334 | 335 | [package.dependencies] 336 | cookiecutter = ">=1.4.0" 337 | pytest = ">=3.3.0" 338 | 339 | [[package]] 340 | name = "python-dateutil" 341 | version = "2.8.1" 342 | description = "Extensions to the standard Python datetime module" 343 | category = "dev" 344 | optional = false 345 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 346 | 347 | [package.dependencies] 348 | six = ">=1.5" 349 | 350 | [[package]] 351 | name = "python-slugify" 352 | version = "4.0.0" 353 | description = "A Python Slugify application that handles Unicode" 354 | category = "dev" 355 | optional = false 356 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 357 | 358 | [package.dependencies] 359 | text-unidecode = ">=1.3" 360 | 361 | [package.extras] 362 | unidecode = ["Unidecode (>=1.1.1)"] 363 | 364 | [[package]] 365 | name = "pytz" 366 | version = "2020.1" 367 | description = "World timezone definitions, modern and historical" 368 | category = "dev" 369 | optional = false 370 | python-versions = "*" 371 | 372 | [[package]] 373 | name = "pyyaml" 374 | version = "5.3.1" 375 | description = "YAML parser and emitter for Python" 376 | category = "dev" 377 | optional = false 378 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 379 | 380 | [[package]] 381 | name = "releases" 382 | version = "1.6.3" 383 | description = "A Sphinx extension for changelog manipulation" 384 | category = "dev" 385 | optional = false 386 | python-versions = "*" 387 | 388 | [package.dependencies] 389 | semantic-version = "<2.7" 390 | sphinx = ">=1.3" 391 | 392 | [[package]] 393 | name = "requests" 394 | version = "2.28.1" 395 | description = "Python HTTP for Humans." 396 | category = "dev" 397 | optional = false 398 | python-versions = ">=3.7, <4" 399 | 400 | [package.dependencies] 401 | certifi = ">=2017.4.17" 402 | charset-normalizer = ">=2,<3" 403 | idna = ">=2.5,<4" 404 | urllib3 = ">=1.21.1,<1.27" 405 | 406 | [package.extras] 407 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 408 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 409 | 410 | [[package]] 411 | name = "semantic-version" 412 | version = "2.6.0" 413 | description = "A library implementing the 'SemVer' scheme." 414 | category = "dev" 415 | optional = false 416 | python-versions = "*" 417 | 418 | [[package]] 419 | name = "six" 420 | version = "1.14.0" 421 | description = "Python 2 and 3 compatibility utilities" 422 | category = "dev" 423 | optional = false 424 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 425 | 426 | [[package]] 427 | name = "snowballstemmer" 428 | version = "2.0.0" 429 | description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms." 430 | category = "dev" 431 | optional = false 432 | python-versions = "*" 433 | 434 | [[package]] 435 | name = "sphinx" 436 | version = "5.3.0" 437 | description = "Python documentation generator" 438 | category = "dev" 439 | optional = false 440 | python-versions = ">=3.6" 441 | 442 | [package.dependencies] 443 | alabaster = ">=0.7,<0.8" 444 | babel = ">=2.9" 445 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 446 | docutils = ">=0.14,<0.20" 447 | imagesize = ">=1.3" 448 | importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} 449 | Jinja2 = ">=3.0" 450 | packaging = ">=21.0" 451 | Pygments = ">=2.12" 452 | requests = ">=2.5.0" 453 | snowballstemmer = ">=2.0" 454 | sphinxcontrib-applehelp = "*" 455 | sphinxcontrib-devhelp = "*" 456 | sphinxcontrib-htmlhelp = ">=2.0.0" 457 | sphinxcontrib-jsmath = "*" 458 | sphinxcontrib-qthelp = "*" 459 | sphinxcontrib-serializinghtml = ">=1.1.5" 460 | 461 | [package.extras] 462 | docs = ["sphinxcontrib-websupport"] 463 | lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] 464 | test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] 465 | 466 | [[package]] 467 | name = "sphinx-rtd-theme" 468 | version = "0.4.3" 469 | description = "Read the Docs theme for Sphinx" 470 | category = "dev" 471 | optional = false 472 | python-versions = "*" 473 | 474 | [package.dependencies] 475 | sphinx = "*" 476 | 477 | [[package]] 478 | name = "sphinxcontrib-applehelp" 479 | version = "1.0.2" 480 | description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" 481 | category = "dev" 482 | optional = false 483 | python-versions = ">=3.5" 484 | 485 | [package.extras] 486 | lint = ["docutils-stubs", "flake8", "mypy"] 487 | test = ["pytest"] 488 | 489 | [[package]] 490 | name = "sphinxcontrib-devhelp" 491 | version = "1.0.2" 492 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." 493 | category = "dev" 494 | optional = false 495 | python-versions = ">=3.5" 496 | 497 | [package.extras] 498 | lint = ["docutils-stubs", "flake8", "mypy"] 499 | test = ["pytest"] 500 | 501 | [[package]] 502 | name = "sphinxcontrib-htmlhelp" 503 | version = "2.0.0" 504 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 505 | category = "dev" 506 | optional = false 507 | python-versions = ">=3.6" 508 | 509 | [package.extras] 510 | lint = ["docutils-stubs", "flake8", "mypy"] 511 | test = ["html5lib", "pytest"] 512 | 513 | [[package]] 514 | name = "sphinxcontrib-jsmath" 515 | version = "1.0.1" 516 | description = "A sphinx extension which renders display math in HTML via JavaScript" 517 | category = "dev" 518 | optional = false 519 | python-versions = ">=3.5" 520 | 521 | [package.extras] 522 | test = ["flake8", "mypy", "pytest"] 523 | 524 | [[package]] 525 | name = "sphinxcontrib-qthelp" 526 | version = "1.0.3" 527 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." 528 | category = "dev" 529 | optional = false 530 | python-versions = ">=3.5" 531 | 532 | [package.extras] 533 | lint = ["docutils-stubs", "flake8", "mypy"] 534 | test = ["pytest"] 535 | 536 | [[package]] 537 | name = "sphinxcontrib-serializinghtml" 538 | version = "1.1.5" 539 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." 540 | category = "dev" 541 | optional = false 542 | python-versions = ">=3.5" 543 | 544 | [package.extras] 545 | lint = ["docutils-stubs", "flake8", "mypy"] 546 | test = ["pytest"] 547 | 548 | [[package]] 549 | name = "tabulate" 550 | version = "0.7.5" 551 | description = "Pretty-print tabular data" 552 | category = "dev" 553 | optional = false 554 | python-versions = "*" 555 | 556 | [[package]] 557 | name = "text-unidecode" 558 | version = "1.3" 559 | description = "The most basic Text::Unidecode port" 560 | category = "dev" 561 | optional = false 562 | python-versions = "*" 563 | 564 | [[package]] 565 | name = "tomli" 566 | version = "2.0.1" 567 | description = "A lil' TOML parser" 568 | category = "dev" 569 | optional = false 570 | python-versions = ">=3.7" 571 | 572 | [[package]] 573 | name = "tqdm" 574 | version = "4.46.0" 575 | description = "Fast, Extensible Progress Meter" 576 | category = "dev" 577 | optional = false 578 | python-versions = ">=2.6, !=3.0.*, !=3.1.*" 579 | 580 | [package.extras] 581 | dev = ["argopt", "py-make (>=0.1.0)", "pydoc-markdown", "twine"] 582 | 583 | [[package]] 584 | name = "typing-extensions" 585 | version = "4.4.0" 586 | description = "Backported and Experimental Type Hints for Python 3.7+" 587 | category = "dev" 588 | optional = false 589 | python-versions = ">=3.7" 590 | 591 | [[package]] 592 | name = "urllib3" 593 | version = "1.26.12" 594 | description = "HTTP library with thread-safe connection pooling, file post, and more." 595 | category = "dev" 596 | optional = false 597 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" 598 | 599 | [package.extras] 600 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] 601 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] 602 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 603 | 604 | [[package]] 605 | name = "zipp" 606 | version = "1.2.0" 607 | description = "Backport of pathlib-compatible object wrapper for zip files" 608 | category = "dev" 609 | optional = false 610 | python-versions = ">=2.7" 611 | 612 | [package.extras] 613 | docs = ["jaraco.packaging (>=3.2)", "rst.linker (>=1.9)", "sphinx"] 614 | testing = ["func-timeout", "jaraco.itertools", "pathlib2", "unittest2"] 615 | 616 | [metadata] 617 | lock-version = "1.1" 618 | python-versions = ">=3.7,<4" 619 | content-hash = "9a12983a44c272239ea2560cc072c6ae221def344671d6de46e2ce48361aed80" 620 | 621 | [metadata.files] 622 | alabaster = [ 623 | {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, 624 | {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, 625 | ] 626 | arrow = [ 627 | {file = "arrow-0.13.2-py2.py3-none-any.whl", hash = "sha256:002f2315cf4c8404de737c42860441732d339bbc57fee584e2027520e055ecc1"}, 628 | {file = "arrow-0.13.2.tar.gz", hash = "sha256:82dd5e13b733787d4eb0fef42d1ee1a99136dc1d65178f70373b3678b3181bfc"}, 629 | ] 630 | attrs = [ 631 | {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, 632 | {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, 633 | ] 634 | babel = [ 635 | {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, 636 | {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, 637 | ] 638 | binaryornot = [ 639 | {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, 640 | {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, 641 | ] 642 | blessings = [ 643 | {file = "blessings-1.7-py2-none-any.whl", hash = "sha256:caad5211e7ba5afe04367cdd4cfc68fa886e2e08f6f35e76b7387d2109ccea6e"}, 644 | {file = "blessings-1.7-py3-none-any.whl", hash = "sha256:b1fdd7e7a675295630f9ae71527a8ebc10bfefa236b3d6aa4932ee4462c17ba3"}, 645 | {file = "blessings-1.7.tar.gz", hash = "sha256:98e5854d805f50a5b58ac2333411b0482516a8210f23f43308baeb58d77c157d"}, 646 | ] 647 | bump2version = [ 648 | {file = "bump2version-1.0.0-py2.py3-none-any.whl", hash = "sha256:477f0e18a0d58e50bb3dbc9af7fcda464fd0ebfc7a6151d8888602d7153171a0"}, 649 | {file = "bump2version-1.0.0.tar.gz", hash = "sha256:cd4f3a231305e405ed8944d8ff35bd742d9bc740ad62f483bd0ca21ce7131984"}, 650 | ] 651 | certifi = [ 652 | {file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"}, 653 | {file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"}, 654 | ] 655 | chardet = [ 656 | {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, 657 | {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, 658 | ] 659 | charset-normalizer = [ 660 | {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, 661 | {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, 662 | ] 663 | click = [ 664 | {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, 665 | {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, 666 | ] 667 | colorama = [ 668 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 669 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 670 | ] 671 | cookiecutter = [ 672 | {file = "cookiecutter-2.1.1-py2.py3-none-any.whl", hash = "sha256:9f3ab027cec4f70916e28f03470bdb41e637a3ad354b4d65c765d93aad160022"}, 673 | {file = "cookiecutter-2.1.1.tar.gz", hash = "sha256:f3982be8d9c53dac1261864013fdec7f83afd2e42ede6f6dd069c5e149c540d5"}, 674 | ] 675 | docutils = [ 676 | {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, 677 | {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, 678 | ] 679 | enum34 = [ 680 | {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, 681 | {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, 682 | {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, 683 | ] 684 | exceptiongroup = [ 685 | {file = "exceptiongroup-1.0.0rc9-py3-none-any.whl", hash = "sha256:2e3c3fc1538a094aab74fad52d6c33fc94de3dfee3ee01f187c0e0c72aec5337"}, 686 | {file = "exceptiongroup-1.0.0rc9.tar.gz", hash = "sha256:9086a4a21ef9b31c72181c77c040a074ba0889ee56a7b289ff0afb0d97655f96"}, 687 | ] 688 | idna = [ 689 | {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, 690 | {file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, 691 | ] 692 | imagesize = [ 693 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, 694 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, 695 | ] 696 | importlib-metadata = [ 697 | {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"}, 698 | {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"}, 699 | ] 700 | iniconfig = [ 701 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 702 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 703 | ] 704 | invocations = [ 705 | {file = "invocations-1.4.0-py2.py3-none-any.whl", hash = "sha256:027f7e2dc3351999c99981c9fc7f70840e966aba7b866d8838bd1959432a1300"}, 706 | {file = "invocations-1.4.0.tar.gz", hash = "sha256:b0b5e8c2a62b7b4267a31ff0c2ae4544fd97dcc70ac6081400f4fb530261a07c"}, 707 | ] 708 | invoke = [ 709 | {file = "invoke-1.4.1-py2-none-any.whl", hash = "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134"}, 710 | {file = "invoke-1.4.1-py3-none-any.whl", hash = "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132"}, 711 | {file = "invoke-1.4.1.tar.gz", hash = "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d"}, 712 | ] 713 | jinja2 = [ 714 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 715 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 716 | ] 717 | jinja2-time = [ 718 | {file = "jinja2-time-0.2.0.tar.gz", hash = "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40"}, 719 | {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"}, 720 | ] 721 | markupsafe = [ 722 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, 723 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, 724 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, 725 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, 726 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, 727 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, 728 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, 729 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, 730 | {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, 731 | {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, 732 | {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, 733 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, 734 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, 735 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, 736 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, 737 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, 738 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, 739 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, 740 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, 741 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, 742 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, 743 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, 744 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, 745 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, 746 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, 747 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, 748 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, 749 | {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, 750 | {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, 751 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, 752 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, 753 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, 754 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, 755 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, 756 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, 757 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, 758 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, 759 | {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, 760 | {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, 761 | {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, 762 | ] 763 | packaging = [ 764 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 765 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 766 | ] 767 | pluggy = [ 768 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 769 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 770 | ] 771 | pygments = [ 772 | {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, 773 | {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, 774 | ] 775 | pyparsing = [ 776 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 777 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 778 | ] 779 | pytest = [ 780 | {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, 781 | {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, 782 | ] 783 | pytest-cookies = [ 784 | {file = "pytest-cookies-0.6.1.tar.gz", hash = "sha256:94c41ad914d420b57bc31d58ab2c0b103322c11d07d13b8d245c85fa9b069714"}, 785 | {file = "pytest_cookies-0.6.1-py3-none-any.whl", hash = "sha256:2e2383c12a321753676c8944fb75b3c8ccac70fce58e27d6b201e1a3ff3dab27"}, 786 | ] 787 | python-dateutil = [ 788 | {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, 789 | {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, 790 | ] 791 | python-slugify = [ 792 | {file = "python-slugify-4.0.0.tar.gz", hash = "sha256:a8fc3433821140e8f409a9831d13ae5deccd0b033d4744d94b31fea141bdd84c"}, 793 | ] 794 | pytz = [ 795 | {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"}, 796 | {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, 797 | ] 798 | pyyaml = [ 799 | {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, 800 | {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, 801 | {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, 802 | {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, 803 | {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, 804 | {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, 805 | {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, 806 | {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, 807 | {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, 808 | {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, 809 | {file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"}, 810 | {file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"}, 811 | {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, 812 | ] 813 | releases = [ 814 | {file = "releases-1.6.3-py2.py3-none-any.whl", hash = "sha256:cb3435ba372a6807433800fbe473760cfa781171513f670f3c4b76983ac80f18"}, 815 | {file = "releases-1.6.3.tar.gz", hash = "sha256:555ae4c97a671a420281c1c782e9236be25157b449fdf20b4c4b293fe93db2f1"}, 816 | ] 817 | requests = [ 818 | {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, 819 | {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, 820 | ] 821 | semantic-version = [ 822 | {file = "semantic_version-2.6.0-py3-none-any.whl", hash = "sha256:2d06ab7372034bcb8b54f2205370f4aa0643c133b7e6dbd129c5200b83ab394b"}, 823 | {file = "semantic_version-2.6.0.tar.gz", hash = "sha256:2a4328680073e9b243667b201119772aefc5fc63ae32398d6afafff07c4f54c0"}, 824 | ] 825 | six = [ 826 | {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, 827 | {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, 828 | ] 829 | snowballstemmer = [ 830 | {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"}, 831 | {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, 832 | ] 833 | sphinx = [ 834 | {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, 835 | {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, 836 | ] 837 | sphinx-rtd-theme = [ 838 | {file = "sphinx_rtd_theme-0.4.3-py2.py3-none-any.whl", hash = "sha256:00cf895504a7895ee433807c62094cf1e95f065843bf3acd17037c3e9a2becd4"}, 839 | {file = "sphinx_rtd_theme-0.4.3.tar.gz", hash = "sha256:728607e34d60456d736cc7991fd236afb828b21b82f956c5ea75f94c8414040a"}, 840 | ] 841 | sphinxcontrib-applehelp = [ 842 | {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, 843 | {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, 844 | ] 845 | sphinxcontrib-devhelp = [ 846 | {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, 847 | {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, 848 | ] 849 | sphinxcontrib-htmlhelp = [ 850 | {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, 851 | {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, 852 | ] 853 | sphinxcontrib-jsmath = [ 854 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, 855 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, 856 | ] 857 | sphinxcontrib-qthelp = [ 858 | {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, 859 | {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, 860 | ] 861 | sphinxcontrib-serializinghtml = [ 862 | {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, 863 | {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, 864 | ] 865 | tabulate = [ 866 | {file = "tabulate-0.7.5.tar.gz", hash = "sha256:9071aacbd97a9a915096c1aaf0dc684ac2672904cd876db5904085d6dac9810e"}, 867 | ] 868 | text-unidecode = [ 869 | {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, 870 | {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, 871 | ] 872 | tomli = [ 873 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 874 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 875 | ] 876 | tqdm = [ 877 | {file = "tqdm-4.46.0-py2.py3-none-any.whl", hash = "sha256:acdafb20f51637ca3954150d0405ff1a7edde0ff19e38fb99a80a66210d2a28f"}, 878 | {file = "tqdm-4.46.0.tar.gz", hash = "sha256:4733c4a10d0f2a4d098d801464bdaf5240c7dadd2a7fde4ee93b0a0efd9fb25e"}, 879 | ] 880 | typing-extensions = [ 881 | {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, 882 | {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, 883 | ] 884 | urllib3 = [ 885 | {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, 886 | {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, 887 | ] 888 | zipp = [ 889 | {file = "zipp-1.2.0-py2.py3-none-any.whl", hash = "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"}, 890 | {file = "zipp-1.2.0.tar.gz", hash = "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1"}, 891 | ] 892 | --------------------------------------------------------------------------------