Project Case Study
pyproject-init
A command-line tool for creating ready-to-develop Python projects with a consistent modern setup.
- Python
- Click
- Cookiecutter
- uv
- pytest
- Ruff
- MyPy
Why I Built It
I found myself making many of the same setup decisions whenever I started a Python project: how to structure the package, which tools to use, how to configure testing and linting, and which development commands should be standard.
pyproject-init turns those repeated decisions into a reusable starting point. Instead of rebuilding the same foundation every time, I can create a project with a consistent baseline and spend more time on the actual problem the project is meant to solve.
What It Generates
The default template creates a src-layout Python project with a small but practical development setup.
- uv for Python versions, environments, dependencies, and commands
- uv_build for packaging
- pytest and pytest-cov
- Ruff for linting and formatting
- MyPy for static type checking
- A .python-version file
- A practical .gitignore
- An MIT license
- A starter CLI application
- A starter test
The generated project also separates the distribution name from the importable Python package name, allowing names such asmy-cool-app to become a valid package likemy_cool_app.
Engineering Decisions
One of the main design goals is to provide useful defaults without turning the generated project into an oversized framework.
The default template is deliberately opinionated about core development tooling, but intentionally small enough to remain understandable and adaptable.
- Existing destination directories are rejected instead of overwritten, reducing the risk of destructive behavior.
- Python version selection is written into
.python-versionand becomes the generated project's minimum supported Python version. - The generated README includes the project's normal development commands so the resulting repository is usable immediately.
- The template focuses on a solid development baseline rather than attempting to generate every possible open-source or governance file.
Quality and Verification
I wanted the project to verify more than whether the generator itself runs successfully.
The repository includes routine automated checks along with smoke tests that exercise a freshly generated project and the built pyproject-init package.
Continuous integration currently validates Python 3.12, 3.13, and 3.14, checks the generated project, and verifies the built wheel and bundled template.
Current Limitations
- The bundled default template is currently the primary supported generation path.
- Custom-template support exists as a direction for the project but is not yet a fully established workflow.
- Existing destination directories are rejected rather than merged into or overwritten.
- Generated projects intentionally stop short of providing a complete open-source governance setup.
What I Learned
The project has made me think more carefully about what actually belongs in a reusable Python project baseline. A tool like this has to balance consistency with flexibility: too little structure defeats the purpose, while too much structure makes the generated project harder to understand and adapt.
It has also reinforced the importance of testing generated output directly. A scaffolding tool can pass its own unit tests while still producing a broken project, so validating the generated repository is an important part of the design.
Current Status
pyproject-init is currently at version 0.2.0 and is classified as beta. It supports CPython 3.12, 3.13, and 3.14.
I am continuing to refine the generated project structure, developer experience, documentation, and template behavior as I use the tool in my own projects.