- Choose a name that does not exist in pypi. You can check by going to
https://pypi.org/simple/{my_awesome_new_package}; a 404 means the name is available. - Run the personalization script. It rewrites every place the original author's identity is baked in (package name, GitHub username, author name, email, ORCID, codecov token) and renames the
my_package/directory in one shot:
scripts/personalize.sh \
--name my_awesome_new_package \
--username my-github-handle \
--author "Jane Doe" \
--email jane@example.com \
--orcid 0000-0001-2345-6789 # optional; line is dropped from CITATION.cff if omittedReview the diff (git diff), then commit. Once you're satisfied you can delete the script with rm -r scripts/.
- Replace the placeholder
LICENSE.mdwith the full text of your chosen license (see choosealicense.com) and update thelicensefield inpyproject.tomlaccordingly. Per PEP 639, prefer an SPDX expression:license = "MIT". - Replace
docs/_static/logo.pngandlogo_transparent.pngwith your own art (or removehtml_logofromdocs/conf.py). The committed art is AI-generatedmy_packageplaceholder. - Once codecov is enabled for the new repo, replace
REPLACE_WITH_CODECOV_TOKENinREADME.mdanddocs/index.rstwith your project's token. - Get coding! Follow the best practices below. When you are ready to publish, proceed to the next step.
- Before tagging a release, update all of:
__version__inmy_package/__init__.pyversion:anddate-released:inCITATION.cff(and thedoi:once Zenodo mints it)- the
Development Statusclassifier inpyproject.toml
- When finished, first enable preservation of this repo on Zenodo, then create a release on GitHub. Zenodo will detect the new release and mint a DOI and badge. Update the
{github_id}placeholder in thisREADME.mdand indocs/index.rstwith the ID generated by Zenodo. - Follow these instructions to publish to PyPI.
- The included GitHub Actions workflows trigger on pushes and PRs to
main. If you adopt adev→mainpattern for releases, update theon:blocks in.github/workflows/*.ymlso CI runs ondevtoo.
Create a conda/mamba environment, then install the package in editable mode so changes are picked up immediately during development.
mamba create -n awesome_env python=3.12
mamba activate awesome_env
cd my_awesome_new_package
pip install -e ".[dev]" # editable install with dev/test/docs/notebook extras
python -m ipykernel install --user --name=awesome_env # register Jupyter kernel
jupyter notebook --port 4321 # launch on localhost (default auth enabled)Or, using uv (a fast drop-in for pip and venv from Astral):
uv venv awesome_env --python 3.12 # create virtual env in ./awesome_env
source awesome_env/bin/activate # Windows: awesome_env\Scripts\activate
cd my_awesome_new_package
uv pip install -e ".[dev]" # editable install with dev/test/docs/notebook extras
python -m ipykernel install --user --name=awesome_env # register Jupyter kernel
jupyter notebook --port 4321 # launch on localhost (default auth enabled)The package declares the following extras in pyproject.toml. The default
dependencies list is empty on purpose — add only what your package
actually imports. The science extra is opt-in so users who don't need it
aren't forced to install hundreds of MB of wheels.
science—matplotlib,numpy,pandas,scikit-learn,scipy,seabornwith loose floorstest—pytest+pytest-cov(what CI installs)docs— Sphinx and the theme/extensions used bydocs/conf.pynotebook—IPython,ipywidgets,ipykerneldev— superset of the above pluspre-commitandmypy
Note: don't disable Jupyter's token/password or bind it to 0.0.0.0 unless you understand the access implications. The default (localhost + token) is the safe choice.
Documentation lives in docs/ and is built with Sphinx. Sphinx and the extensions enabled in docs/conf.py are declared as the docs extra in pyproject.toml, so a single editable install pulls everything in:
pip install -e ".[docs]"
cd docs
bash make_docs.shAdjust the landing page in docs/index.rst manually — see the reStructuredText primer.
For hosted docs, link the repo at Read the Docs. The .readthedocs.yml installs the package with its docs extra so the build matches the pinned toolchain in pyproject.toml.
To start fresh, run sphinx-quickstart in docs/.
Add unittests in tests/. pyproject.toml configures pytest to look there automatically.
python -m pytest.github/workflows/python-app.yml runs these tests and reports coverage on every push and pull request to main. Edit the on: triggers if you want the workflow to run on other branches too.
Code coverage is wired to codecov.io. Enable the repo there, add your CODECOV_TOKEN as a GitHub Actions secret, and update the badge URL in this README.md and in docs/index.rst (find it under Configuration → Badges & Graphs on codecov).
The template ships CI for both GitHub and GitLab so you can host on either
without rewriting pipelines. The two configs are kept behaviorally equivalent
for the test stage (same Python matrix, same install command, same coverage
report). When you change one, mirror the change in the other; both files
have a KEEP IN SYNC header pointing at the other.
GitHub-specific workflows (with no GitLab equivalent in this template):
.github/workflows/python-app.yml— test matrix on 3.10–3.13, coverage to Codecov..github/workflows/pre-commit.yml— runs pre-commit hooks on push and PR tomain..github/workflows/codeql.yml— CodeQL static analysis on push, PR, and weekly Mondays. Findings appear under Security → Code scanning..github/dependabot.yml— weekly PRs to update GitHub Actions and pip dependencies. Tuneintervaloropen-pull-requests-limitif volume is too high.
GitLab equivalent:
.gitlab-ci.yml— mirrors the test matrix and addspre-commitandmypyjobs. CodeQL has no GitLab analogue here; if you host on GitLab, consider GitLab's built-in SAST instead.- No Dependabot on GitLab. If you host on GitLab, consider Renovate (works on both platforms) or replace
.github/dependabot.ymlwith a manual update cadence.
Linting and formatting are handled by ruff via pre-commit. See .pre-commit-config.yaml for the full hook configuration.
Install the hooks once, then they'll run on every commit:
pre-commit installTo run all hooks against the whole repo on demand:
pre-commit run --all-filesType hints are optional but recommended — see the typing docs and mypy. Mypy is configured under [tool.mypy] in pyproject.toml and runs in CI on every push/PR (the GitHub python-app.yml test job runs it on Python 3.12; the GitLab mypy job runs in the lint stage). The package ships a PEP 561 py.typed marker so downstream consumers see your annotations.
Run mypy locally the same way CI does:
pip install -e ".[dev]"
mypy my_packageThe default config is permissive (ignore_missing_imports, check_untyped_defs, no disallow_untyped_defs) so a fresh template checkout passes without effort. Tighten it as your code grows — e.g., flip on disallow_untyped_defs = true once you're ready to require annotations on public APIs.
If your package benefits from a UI, consider a Streamlit demo (free hosting via the community cloud) or a Hugging Face Space. Either works directly from a public repo.
Generate a logo with Google Gemini or any other AI tool, save it under docs/_static/, and point the html_logo setting in docs/conf.py at the new file. If you use an AI-generated image, cite it — name the tool, the company, and the prompt.
The logo for this repository (docs/_static/logo.png) was generated with Google Gemini (Imagen 3) on Nov. 28, 2024 from the prompt: "Make a logo inspired by code as a template for python projects."
Update CITATION.cff, CODEOWNERS, and pyproject.toml to list all authors and maintainers. The CITATION format is documented at citation-file-format.github.io.