I’ve published a lock-defaults package. This is mostly a little toy function that gets rid of the need for an annoying python pattern.

Can be installed via pip or just by copy and pasting from GitHub (only depends on standard library).

I’m using this project to explore useful approaches to pull into a standard template for a python library.

Will write that up more later, but I’m trying an approach where if there’s a GitHub Action that checks for a difference between the version of the package on pypi, and the version in poetry. If there is one, it publishes the new version automatically. Before it does that, it runs pytest, which includes a check that the changelog has been updated for the new version.

What’s this package do?

Python has a weird behaviour around default values for functions. If you use an empty list as a default argument, things added to the list during the function can hang around for next time the function is called. A common pattern of dealing with this is the following:

def func(foo = None):
    if foo is None:
        foo = []

But this looks rubbish! And gets worse when you add typing:

def func(foo: list | None = None):
    if foo is None:
        foo = []

You don’t need that workaround for any other of default value. Why does the list parameter have to pretend it can be None, when that’s not the intention at all?

The lockmutabledefaults decorator fixes this by introducing what should be the default approach, and default values that are lists, dictionaries or sets are isolated in each re-run.

@lockmutabledefaults
def func(foo: list = []):
    pass