Self-hosted service
microsoft/python-package-template avatar
microsoft/python-package-template

Microsoft's python-package-template: a PEP 621 skeleton with every tool pre-wired

Template for Python Projects

592 stars195 forksPythonMIT

At a glance

What is it?
The python-package-template repository is a starting point for a Python project built on PEP 621 metadata and Flit, with black, flake8, pylint, pyright, pytest, coverage, tox, bandit and pre-commit already configured in one pyproject.toml. It is a template, and it carries decisions you did not make.
Who is it for?
python-package-template suits a team starting a new Python library that wants metadata, linting, type checking, tests and publishing wired up in one file before the first commit, and it suits a developer who wants to see what a fully configured pyproject.toml looks like.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 63 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One file holds the metadata, the tools and the build

The python-package-template repository is a template for creating Python projects that follow the Python Standards declared in PEP 621. It uses a pyproject.toml file to configure the project and Flit to simplify the build process and publish to PyPI. The stated benefit of Flit is that it eliminates the need for separate setup.py and setup.cfg files, so all relevant configurations live in the pyproject.toml, centralising project metadata, dependencies and build specifications in one place. The README's description of the layout makes the intent clear. The pyproject.toml contains metadata about the project and configurations for additional tools used to format, lint, type-check and analyse Python code, and its key components are project metadata, required and optional dependencies, development tool configurations such as linters, formatters and test runners, and build system specifications. The build-system section is three lines long and names the backend:

toml
[build-system]
requires = ["flit_core >=2,<4"]
build-backend = "flit_core.buildapi"

Everything else about the packaging, from the module name to the classifier list to the project URLs, is a section in that same file, which is the central design claim of the template and the reason it is worth reading even if you never use it directly.

Coverage that fails the build below 100 percent

The tool configuration is where a template either helps or gets in the way, and the coverage setting is the one that will get a team's attention first. The coverage section enables branch coverage and sets a failure threshold:

toml
[tool.coverage.run]
branch = true
toml
[tool.coverage.report]
fail_under = 100

The README explains that coverage measures which lines were executed during testing and which were not, that branch coverage should be measured, and that the tests should fail if the coverage falls below 100 percent. That is an unusually strict default, and it is the setting most likely to be the first thing you change. It also encodes a real philosophy. A 100 percent floor with branch coverage means every line runs and every branch is taken, which is a strong guarantee against dead code, but it also means the template arrives with a test suite shape that assumes small, fully exercised modules. Applied to a codebase with third-party integration code that can only be exercised against a live service, the floor becomes a tax rather than a signal, and the usual outcome is that a team raises the number and loses the value of the default. The bandit section is worth noting too, since it excludes the build, dist, tests and scripts directories, sets a number threshold of 4, is recursive, and targets the src directory.

The formatter and the linters disagree about line length on purpose

The template ships a formatter and three linters, and their settings are not identical, which is the detail worth knowing before you fight your editor. Black is the formatter, and it automatically reformats Python code to conform to the PEP 8 style guide to keep code style consistent across the project. Its section sets the maximum line length and whether to use the fast mode for formatting, and the README notes that Black allows a pyproject.toml configuration file to be included in the project directory to customise its behaviour. The settings are concrete:

toml
[tool.black]
line-length = 120
fast = true

Flake8 is a separate linter that checks for style and syntax issues, and its configuration covers the maximum line length, which errors to ignore, and which style guide to follow. Pylint adds static analysis on top, with a report of errors, warnings and conventions, and its configuration in pyproject.toml covers extension management, warning suppression, output formatting and code style settings such as the maximum number of function arguments and class attributes. Pylint also brings its own scoring system, which the README presents as a way to assess and maintain code quality with a focus on readability. Pyright is the type checker, using type annotations to catch type-related errors, and it can also work from docstring types, with its configuration covering directories to include or exclude, the virtual environment to use, and settings for reporting missing imports and type stubs. Four tools checking the same file is a lot of surface area, and the black and flake8 line length settings have to agree or the two will fight over the same line.

Pytest markers and tox environments decide what runs where

The test setup is the part of the template with the most opinion in it, and it is worth understanding before you accept it. Pytest is the test framework, supporting both pytest-style and unittest-style tests, with fixtures for clean environments, parameterised tests to reduce duplication, and plugin extensibility. Its configuration in pyproject.toml includes test markers, and the README lists them: integration, notebooks, gpu, spark, slow and unit. The markers are how you tell the template which tests belong to which category. Tox then automates testing and building the package across environments and versions, and the README says the Tox configuration sets up four testing environments: py, integration, spark and all, each targeting specific test categories or running all tests together. The [tool.tox] section in pyproject.toml holds the details, including the legacy_tox_ini attribute, which is the migration path from the old ini-style configuration to the native pyproject form. The four environments map onto the marker scheme. The py environment is the plain unit test run, the integration environment is for the integration marker, spark is for the optional pyspark dependency, and all runs the whole set. The optional dependency that backs it is declared separately, with spark pulling in pyspark.

The devcontainer and the CI workflows are the parts you copy wholesale

The repository layout shows what a team gets beyond the pyproject.toml. The .github/workflows directory contains the GitHub Actions used for building, testing and publishing, and the .devcontainer directory holds a Dockerfile to build a development container for VSCode with the necessary Python extensions installed, plus a devcontainer.json covering the Docker image to use, additional VSCode extensions to install, and whether to mount the project directory into the container. The .vscode/settings.json holds project-specific settings such as the Python interpreter to use and the maximum line length for auto-formatting. On the testing side, the test extra in pyproject.toml pins a substantial toolchain, including bandit with toml support, black, check-manifest, flake8-bugbear, flake8-docstrings, flake8-formatter_junit_xml, flake8-pyproject, pre-commit, pylint with its junit reporter, pytest with coverage, mock and runner extras, the GitHub Actions annotation plugin, and shellcheck-py. The presence of check-manifest and shellcheck-py is a good signal about the intended standard, since the first checks that the files you ship match what you declared, and the second lints shell scripts inside a Python project. The repository also carries a .pre-commit-config.yaml at the root, which is where the pre-commit hooks are defined, and a .pypirc for publishing configuration.

The version is dynamic, and the Python floor is 3.8

Two details in the project metadata will trip you up if you copy the file without thinking. The first is that the version is dynamic, declared as dynamic = ["version"], with no static version string in the project section. With Flit as the backend, that means the version comes from the module itself rather than from pyproject.toml, which is a reasonable choice since it keeps a single source of truth, but it does mean the version is not where a reader expects to find it. The second is the Python range. The project requires Python 3.8.1 or newer, and the classifiers list 3.8, 3.9, 3.10 and 3.11, with the 3.8 classifier and the requires-python floor of 3.8.1 slightly out of step with each other, and no classifier for anything newer than 3.11. The pinned dev tool versions are of the same vintage, black at 23.3.0, pylint at 2.17.4 and pytest at 7.3.1, all of which predate current releases. The project is MIT licensed, the last push to the repository was on 2026-08-21, and the single published release is 1.0.0, titled as the pyproject.toml template initial release and dated 2022-05-02, so the release history tells you the template was cut from a single snapshot while the repository itself has continued to receive commits since.

Editorial conclusion

python-package-template suits a team starting a new Python library that wants metadata, linting, type checking, tests and publishing wired up in one file before the first commit, and it suits a developer who wants to see what a fully configured pyproject.toml looks like. It is the wrong choice for an application rather than a library, since the template is built around package publishing to PyPI, and it is the wrong choice if your project cannot meet a 100 percent coverage floor. Verify first that your supported Python versions match the classifiers, that you can hold the line on the coverage threshold, and that the pinned tool versions in the test extra still run on your interpreter.

Frequently asked questions

What does the Microsoft python-package-template include?

A pyproject.toml following PEP 621 with Flit as the build backend, a src/ directory for new source code, a tests/ directory, GitHub Actions workflows for building, testing and publishing, a devcontainer for VSCode, and a .vscode/settings.json. Tool configs for black, coverage, flake8, pylint, pyright, pytest, tox and bandit live in the same pyproject.toml.

What coverage threshold does python-package-template enforce?

The coverage report section sets fail_under to 100, and branch coverage is enabled. The README states that the tests should fail if coverage falls below 100 percent.

Which Python versions does python-package-template support?

The project metadata sets requires-python to 3.8.1 or newer, and the classifiers list Python 3.8, 3.9, 3.10 and 3.11 with Python 3 only.

How do the tox environments in python-package-template differ?

The README describes four environments configured through the tool.tox section: py, integration, spark and all, each targeting specific test categories or running all tests together, with details held in pyproject.toml including the legacy_tox_ini attribute.

Official sources

  1. Issues
  2. License: MIT
  3. microsoft/python-package-template on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/microsoft-python-package-template.svg)](https://hysenlabs.com/projects/microsoft-python-package-template)

Was this useful?