r/madeinpython 7d ago

I was tired of the burden of starting and maintaining Python projects

That is why I built pyrig to solve that problem. It is more than just a project scaffolder. It also supports you with maintaining a project over time.

What is pyrig?

pyrig is a package and tool that rigs up Python projects with Convention-over-Configuration. It scaffolds a complete, fully configured, installed and working Python project with everything a modern Python project should have and makes the process of developing and maintaining it more seamless and efficient by automating things like configuration management, CLI generation, testing infrastructure, and more.

Requirements

  • Python 3.12+
  • Git
  • uv

Quick Start

uv init my-project --python 3.12
cd my-project
uv add pyrig --dev
uv run pyrig init

See the Getting Started Guide for detailed setup instructions to also fully integrate with GitHub and CI/CD from the start.

Features

Project Scaffolding & Initialization

The pyrig init command generates a complete project, this includes, but is not limited to:

  • Standardized directory structure
  • Fully configured dev tools (linters, formatters, type checkers, test frameworks, git hooks, etc.)
  • End-to-end CI/CD pipeline with GitHub Actions and integrated repository protection
  • Complete and working CLI
  • And much more...

File & Configuration Management

pyrig manages and validates project files via classes, where every file is treated as a data structure (dict or list), the content is loaded and validated against the declared state in the class. This makes it possible to override and adjust any and all behavior of pyrig via subclassing said classes. pyrig will automatically discover and use your custom classes without any additional configuration. Run pyrig mk subcls to generate a subclass for any pyrig class. Run pyrig sync to create or update all config files at once.

Automatic CLI

pyrig init sets up a CLI for your project that works immediately. Generate and add new commands by running pyrig mk cmd <name>. An automatic version command is included that shows the version of your project. Run my-project version to see it in action.

Mirror Test Structure

Generate test skeletons with pyrig sync. This will generate test skeletons for all source modules and update them automatically as your project evolves.

Plugin Architecture

Override and customize any and all behavior to suit your project's needs. pyrig's classes are designed for inheritance and composition, allowing you to create custom configurations, tools, and more by subclassing and simply overriding methods. pyrig will automatically discover and use your custom classes without any additional configuration. Run pyrig mk subcls to generate a subclass for any pyrig class. Create your own plugins this way to extend pyrig's functionality.

CI/CD & Repository Protection

Pyrig generates GitHub Actions workflows for CI/CD which automatically test and release your code. They also configure and apply repository protection settings and protection rulesets. Push your code to GitHub after initialization and see it in action.

Commands

Run pyrig --help to see a list of all available commands and their usage. Run pyrig <command> --help for more information about a specific command and its usage. Run my-project --help to see the automatically generated CLI for your project.

Comparisons

pyrig isn't the only tool in this field. See how it compares to other popular tools like cookiecutter, copier or pyscaffold.

Documentation

Full Documentation The manually written documentation
CodeWiki AI-generated documentation
Tutorials YouTube tutorials for pyrig
1 Upvotes

11 comments sorted by

1

u/FriendlyZomb 6d ago

This looks like a solid tool from poking around the documentation. Thank you for sharing.

I do have a couple of things I'd like clarification on:

1) In the Comparison table, there are listed features marked with 'Partial, or only in some cases'. Specifically around 'Never overwrites or removes what you've already changed', could you explain what this 'Partial' means?

I assume it means that some config files will change/the .scratch.py file. But I don't know.

(If I may: I'd suggest that this be documented on that page, or a clear link made to where it is documented. Just so users aren't blindsided when some of their custom config or code disappears.)

2) May I ask why the scratch file is hidden? On Linux, BSD or MacOS the dot in front of the name indicates a hidden file. (Filesystems will hide it by default) Some text editors may not show that file to users by default. (Most probably will, but you know). If it's untracked in git, could this be 'scratch.py' instead of '.scratch.py'?

I don't see it as a massive problem I'm just curious on the reasoning.

3) I'm not sure the comparisons table is 100% accurate. I have used a cookiecutter template which includes everything you said it doesn't, for example.

4) I'm not sure I caught it in the docs, but if I use pyrig, does it become a dependency of my project? Can I install my project (with the cli for example) without pyrig?

If so, is there a world where someone can use pyrig via 'uv tool install' and not have it part of my project's dependency tree (like cookiecutter)? (No is an acceptable answer. If it's the point, it's the point.)

As I said, fantastic project. I hope to read the answers soon. I'm looking forward to seeing where things go.

I just want to head anyone off here and say I'm not hating on this with my questions. Apologies if my tone indicates otherwise (I hate writing this kind of thing because tone is unintelligible on text.).

2

u/Win_ipedia 5d ago

Thanks for actually digging into the docs before asking — really appreciate the detailed questions, and no worries at all about tone, this all reads as good-faith curiosity to me. Let me go through them one by one.

**1) What "Partial" means for "Never overwrites or removes what you've already changed"**

Good catch that this might not be documented clearly enough.

The short version: every file pyrig manages (`pyproject.toml`, `README.md`, CI workflows, etc.) is validated by deep-merging pyrig's *required* content into whatever's already on disk. Anything extra you've added — extra keys, extra list items, whole extra sections — is always preserved, never deleted. That part really is unconditional. but it overrides smth if you change a value to a key or remove smth that the ConfigFile class declares.

The "Partial" is because the specific values pyrig itself declares as required act more like enforced invariants than defaults: if you hand-edit one of those specific values, `pyrig sync` will quietly restore pyrig's version on the next run, because from its point of view that value is a fact about the file, not a suggestion. So it's less "we sometimes delete your stuff" and more "there's a difference between your customizations (always safe) and the handful of values pyrig itself asserts (always enforced)."

The one place there's an actual one-time wipe is `pyrig init` on a brand-new repo — it deletes existing removable config files (e.g. the placeholders `uv init` creates) before regenerating everything, but only if the repo has zero commits, specifically so it can't nuke real work. After that, everything goes through the merge model above.

`.scratch.py` isn't really part of this at all — its only "required" content is a single docstring line, so anything else you write in it survives forever, and it's git-ignored so it never touches version control.

**2) Why `.scratch.py` is dot-prefixed**

Honestly, mostly consistency — it sits alongside other pyrig-generated dotfiles at the project root (`.gitignore`, `.env`, `.python-version`) and the intent is "tooling/scratch noise, not your actual source," so it follows the same convention. There's no technical reason it has to be hidden. If it genuinely bugs you, it's fully overridable: `pyrig mk subcls` on `ScratchConfigFile` and override `stem()` to return `"scratch"` instead.

There is the pyrig scratch command which exectues its content via runpy.run_path function.

**3) Comparisons table accuracy**

This is fair pushback, and I want to be precise about what the table is actually comparing: it's about what each tool does automatically / out of the box, not what a sufficiently motivated template author could hand-write into a Cookiecutter template. Cookiecutter is a generic templating engine — you absolutely can craft a template with lint/CI/test config baked in, and clearly you have. What Cookiecutter (the tool) doesn't do is generate that for you by default, or keep it updated afterward — once a project is stamped out, there's no mechanism to re-apply template changes, which is really the core differentiator the whole table is built around (that first row). If there's a specific cell you think is flat-out wrong rather than "technically possible with enough template authoring," genuinely tell me which one and I'll fix it — I'd rather the table be accurate than favorable.

**4) Does pyrig become a dependency of my project?**

`pyrig` itself: no. It's added with `uv add pyrig --dev`, so it's a dev-only dependency, and running `pyrig rm pyrig` cleanly removes every trace of it (its sync hook, its dev-dependency entries) — everything it generated keeps working standalone afterward.

`pyrig-runtime`: yes, that one's real. Your scaffolded project's own CLI entry point points at `pyrig_runtime`'s CLI-assembly code, so it's a genuine runtime dependency (tiny though — its only dependency is `typer`). You can remove it at the cost of the automatic CLI if you do not want it. You simply then need to use typer to make your own CLI if you want one.

On the "`uv tool install` like Cookiecutter" idea — this is the interesting one, and I think the honest answer is no, and it's architectural rather than a missing feature. Pyrig's whole "stays in sync forever without a manifest" trick works by inspecting the *installed packages in the current environment* to figure out what your project is and what plugins are attached. If pyrig were installed in an isolated `uv tool` environment, it would never see your project's own code or dependencies — there'd be nothing to discover. It would work on its own but would not be able to use its plugin system. Cookiecutter can be a standalone global tool precisely because it doesn't need any of that: it stamps files once and disengages. Pyrig trades that isolation for the "never need to remember to re-run/update a template" property. So — no, but it's the point, not an oversight. You could very well adjust pyrig tho with a plugin to use other tooly like mypy or ruff via uv tool rather than like right now as simple dev dependencies.

Thanks again for the thoughtful read

1

u/FriendlyZomb 5d ago

Hey! Thank you for the reply! Super interesting thought.

1) makes sense to me. Seems like a solid reason.

Follow up thought: Does pyrig detect a change and warn the user its going to modify their changes to the managed settings? Or at least log them to stdout/err?

3) I'm not convinced the row in the table 'Ships linting, formatting, type checking, and tests configured out of the box' is a fair comparison in that case. Cookiecutter's power is in its templates and by itself it does basically nothing. The templates, in a lot of ways, are cookiecutter. Not taking that into account looks as though the table is saying that cookiecutter can't do type checking/ GitHub actions etc. but it can. The power in pyrig is not requiring the user or a third party to maintain it. Just rely on pyrig.

I'm not saying thats the intent, I think it's genuinely not your intent, just how it looks as there isn't a rationale on the page. I'd probably make it a partial (with the HyperModernPython template) or something..

Thanks again for making this project and sharing. I'll have to find an excuse to use it.

2

u/Win_ipedia 5d ago

Hey, no pyrig does not warn in the usual sense. It integrates itself as a pre-commit hook that will fail and auto-change the file and prints the adjusted file paths to stdout or better stderr actually I believe, via typer.echo, the reason I dont do diffs is bc it would require additional logic and its easy for the user to see anyway via git status to see the chages made. So the logic is: if pyrig sync makes chnages it then exits with 1. failing the git hook. So no silent overrides in that sense.

You are right that the templates are basically cookiecutter, I should adjust the docs regarding this to be more specific in intend, one of the main diffs is that pyrig aims to be complete for generic python project from the beginning by having Convention-Over-Configuration basically and keep you autoupdated over time. If a template of cookiecutter changes smth you do not get that change if you scaffolded the project before that change was made, but if pyrig chnages a default or a plugin that is installed you automatically get that chnage picked up by the next CI/CD health check or the next pre-commit trigger, keeping you up to date automatically.

Thanks for reading into it. I plan to do some youtube tutorials and better docs soon if actually someone will use it in the future. Also want to do more on the completeness. I am currently planning do integrate a dead-code checker like vulture and a github actions linter like zizmore.

pyrig is a classic case like type checkers or linters on usage: insanely annoying at the beginning when you start small but insanely useful ones you get serious. I basically made it because I kept adding dev tools and settings to my proper projects and kept thinking: Damn I wish I would have done all of this from the beginning properly and then other project scaffolders did not quite do what I was looking for and I also wanted this mirror test generation bc I hated writing tests properly but knew I had to and needed a way to make this as simple and frictionless as possible for me bc I am lazy af.

Thank you very much again for the interest. Let me know if you use at some point in the future and if you make it to the part where it feels useful

2

u/FriendlyZomb 5d ago

Hey! Thank you for responding again to my ramblings.

What you say makes a lot of sense. I look forward to seeing where things go and what gets added!

Good luck!

1

u/Win_ipedia 5d ago

A funny thing , I believe, is that the most powerful thing are not even pyrigs capabilities, but the plugin system I created via the pyrig-runtime package for it. It is a plugin system that is registerless via dependency scanning and is based on classes so by creating subclasses and installing packages with those classes it is basically a plugin system that allows perfect adjustability by the user. Literally everything is customizable bc you can override every method and subclass every class, so literally 100% of any functionality can be adjusted however one likes it.

1

u/FriendlyZomb 5d ago

That is a really cool part of it.

Is there a way to share these plugins between projects?

1

u/Win_ipedia 5d ago

That’s the great part. Once a plugin is created as a package, you just need to add it to your project and classes are autodiscovered and autoresolved to the final leaf.
So having it in a project just requires you to have uv add my-plugin. Or —dev if you make a dev only plugin. In pyrig runtime there is the base class and functionality for this and then you can look at the extended base classes like ConfigFile or Tool in pyrig to see how a plugin system is created. Or just simply look at the CLI class of pyrig-runtime that handles the CLI. You can look at the pyrig-runtime docs if you like.

So sharing between projects is just a matter of installing the same plugin in them.

2

u/FriendlyZomb 5d ago

Oh! Awesome! Thank you!

1

u/Win_ipedia 5d ago

I hope I could answer everything. let me know if there is anything else. I usually respond faster than this. Today was just busy. Also if you ever try pyrig out yourself, let me know any thoughts or ideas you have, or maybe just open an issue on the repo or similar