Skip to content

bnbalsamo/cookiecutter-pypackage

Repository files navigation

cookiecutter-pypackage v0.48.0

CI

My cookiecutter template for python packages

See an example of a generated project here

Table of Contents

Whats it get me?

tl;dr: A CI enabled Python software project with plenty of bells and whistles.

Quickstart

  • Requirements For These Instructions
  • Steps
    • Create a github repo named $YOUR_PROJECT_NAME
    • Enable repository monitoring on readthedocs
    • Enable repository monitoring in pyup
    • $ cookiecutter gh:bnbalsamo/cookiecutter-pypackage
    • Fill in the prompts
    • Complete the setup instructions printed in your terminal after template rendering
    • Begin developing your package!

Cookiecutter Parameters

Parameter Name Default Description
project_name n/a The name of the project. Used for display purposes and as the name of the containing directory and the GitHub repo. Should not contain whitespace.
pip_name $project_name.lower() The "pip name" of the project. This name will appear in commands like pip freeze and (if the project is published on pypi) this name will be used in commands like pip install
module_name $project_name.lower().replace("-","_") The name of the actual python module, eg: what is used in import statements.
version 0.0.1 The version to initialize the project at. This version number should parsable by bump2version
short_description A project. A one line description of your project for use in documentation.
author First Last The name of the author of the software. Used in documentation and the __author__ dunder on your package.
email you@provider.com Included in the README.md, used as the contact email for your pypi package, and included in the package's __email__ dunder.
github_repo_name $project_name Used to derive the github URL of the project
github_username githubber Used to derive the github URL of the project
github_default_branch_name main Used for the CI badge and in the instructions for project bootstrapping
license GNU GPLv3 The license to release the project under
enforce_strong_typing n If set to y mypy will error on untyped defs.
include_link_back_to_cookiecutter y If set to ythe generated project's README.md will include a link back this cookiecutter.

Preconfigured Invoke Tasks

Any of the following can be run off the bat from the project root, via invoke/inv...

$ inv --list
=> Current Working Directory: ...
Available tasks:

  clean                   Clean up all caches and generated artifacts.
  format                  Run all the formatters.
  lint                    Run pre-commit against all files.
  release                 Perform a release to pypi.
  test                    Run the tests.
  build.coverage-report   Build an HTML coverage report.
  build.dists (build)     Build distribution artifacts.
  build.docs              Build the documentation.
  build.zipapp            Use `shiv` to produce a zipapp that includes the dependencies.
  check.todos             Check for `#TODO` comments in the code.
  serve.docs              Serve the docs on localhost:8000. Reload on changes.

Additional information about commands can be obtained by using their --help flag, eg:

$ inv release --help
Usage: inv[oke] [--core-opts] release [--options] [other tasks here ...]

Docstring:
  Perform a release to pypi.

Options:
  -b, --[no-]build
  -c, --[no-]clean
  -p, --prod
  -s, --skip-tests

Uploading to pypi

Review your package before publishing it!

This blog entry provides a good breakdown of uploading a package to pypi.

The commands required to release are provided as an invoke task:

$ inv release --prod  # Omit the --prod flag to publish to test.pypi

Opinions

This template is opinionated (aren't they all, really?) in a couple of ways.

It keeps all of the static analysis tooling dialed up fairly strictly.

This isn't meant to imply that every project should keep the static analysis tooling dialed up so strictly, but rather that if you are loosening controls on your static analysis for a good reason that reason should be documented as a part of your codebase's git history. This helps keep relevant decisions documented with the code.

It causes testing to fail below 80% code coverage.

80% is arbitrary, but I've found it works fairly well for me when starting a project. If while I'm actively developing and the code base is small I fall below 80% coverage I know I've probably slacked off on tests somewhere. As a codebase grows and matures I tend to turn this metric up to whatever reasonable maximum I can hit.

It uses poetry.

Packaging in the larger Python ecosystem is currently undergoing some significant changes, and several tools (or combinations of tools) exist to handle packaging, dependency management, environment management, artifact generation, uploading to package indices, etc.

Poetry provides a couple of key functionalities:

  • A solver which maps dependency constraints to a working combination of dependency versions
  • The ability to have (regular) dependencies, development dependencies, and extras.
  • A lock file, to provide deterministic environment creation, that contains hashes
  • The ability to export a pip-compatible constraints file, that can be used to create environments which contain subsets of dependencies for testing.
  • The ability to store all metadata in pyproject.toml

These functionalities, in combination with the fact that they require almost no custom code (ecept the install_with_constraints function in the noxfile), make poetry my tool of choice for packaging, currently.

It implements a lot of tooling.

Well, thats the whole reason for the use of a cookiecutter, isn't it? I'm of the opinion that even if a project chooses not use every tool in this template, it's easier to rip out a few pieces here and there than it is to properly re-implement them. Thus I've tended to err on the side of including tooling, rather than omitting it.

It uses web service X instead of competing service Y.

My choice of services is informed by:

  • What services I'm comfortable with implementing
  • What services appear to be popular within the python community

If the services this repository implement are (significantly) eclipsed by similar services in the future open an issue and let me know!

It uses semver for versioning.

I'm of the opinion that semver is applicable to the vast majority of projects. If, for whatever reason, it isn't applicable to a given project, changing the versioning scheme along with the justification should be included in the git history.

It uses a "src" layout

I've been convinced.

It uses invoke as a task runner.

The Python ecosystem includes a ton of useful tooling, but remembering the names, options, and flags for every individual command, as well as implementing consistent workflows involving all of them can be a chore.

Invoke provides (in my opinion) a clean, extensible, understandable method for effectively utilizing all of these diverse tools, implementing repeatable workflows, and communicating usage information to contributors.

Credit Where It's Due

Inspiration (and some code) taken from the following:

About

A cookiecutter template for python packages

Resources

License

Stars

Watchers

Forks

Packages

No packages published