A big hassle of maintaining several open-source Python libraries is how to package them to upload them to PyPi. The first time, it wasn't clear to me the best way to do it, so I learned by doing and, on top of that, the recommended techniques were, and still are, changing.

So each time I started a new project, I would copy & paste files from one repo to another, maybe adding a new file. Sometimes I would go back and update the older repos to the latest trends. It was messy.

Recently, I read a post of another Python programmer with the same problem. His solution? Having a project skeleton in a repo and, with a bit of git hackery, merge it to each of your projects. Sounded great...

But it wasn't. Not only the git history got dirty but it required to manually fix merge conflicts every time. No thanks, my old copy & pasting is way easier.

The good thing it's that gave me an idea: I should treat the packaging-related files not as source code but as a read-only output of another program.

The solution

Behold, the Master Mold!

https://github.com/jpscaletti/mastermold

The "Master Mold" is similar to a cookiecutter template... except that I had just updated an older Python library that was more accurate for this kind of job: Copier.

So, for each repo I now have a mm.py file similar to this:

data = {
    "title": "My Awesome Package",
    "name": "awesome",
    "version": "1.0",
    "author": "Juan-Pablo Scaletti",
    "install_requires": [
        # ...
    ],
    # ...etc.
}

def do_the_thing():
    import copier
    copier.copy(
        "../mastermold",  # Path to the local copy of Master Mold
        ".",  # Destination
        data=data,
        exclude=["copier.yml", "README.md", ".git", ".git/*"],
        force=True,
        cleanup_on_error=False
    )

if __name__ == "__main__":
    do_the_thing()

And when I run this script it automagically update the packaging files of the project to whatever the template has, without extra work or side effects. The only think that I have to always remember is to never edit the generated files (because they'll eventually be overwritten), just the mm.py file.

Whenever I want to make a change in the packaging – say to start using poetry
I only need to update the template and run the script for each of the projects 😀 , and that's it (rerunning the tests is also a good idea).

Now it is finally easy to keep all of my projects up-to-date with the features and best practices for packaging.