Adding your own attributes to Python functions and Python typing

June 6, 2025

Every so often I have some Python code where I have a collection of functions and along with the functions, some additional information about them. For example, the functions might implement subcommands and there might be information about help text, the number of command line arguments, and so on. There are a variety of approaches for this, but a very simple one I've tended to use is to put one or more additional attributes on the functions. This looks like:

def dosomething(....):
  [....]
dosomething._cmdhelp_ = "..."

(These days you might use a decorator on the function instead of explicitly attaching attributes, but I started doing this before decorators were as much of a thing in Python.)

Unfortunately, as I've recently discovered this pattern is one that (current) Python type checkers don't really like. For perfectly rational reasons, Python type checkers like to verify that every attribute you're setting on something actually exists and isn't, say, a typo. Functions don't normally have random user-defined attributes and so type checkers will typically complain about this (as I found out in the course of recent experiments with new type checkers).

For new code, there are probably better patterns these days, such as writing a decorator that auto-registers the subcommand's function along with its help text, argument information, and so on. For existing code, this is a bit annoying, although I can probably suppress the warnings. It would be nice if type checkers understood this idiom but adding your own attributes to individual functions (or other standard Python types) is probably so rare there's no real point.

(And it's not as if I'm adding this attribute to all functions in my program, only to the ones that implement subcommands.)

The moral that I draw from this is that old code that I may want to use type-inferring type checkers on (cf) may have problems beyond missing type hints and needing some simple changes to pacify the type checkers. It's probably not worth doing a big overhaul of such code to modernize it. Alternately, perhaps I want to make the code simpler and less tricky, even though it's more verbose to write (for example, explicitly listing all of the subcommand functions along with their help text and other things in a dict). The more verbose version will be easier for me in the future (or my co-workers) to follow, even if it's less clever and more typing up front.


Comments on this page:

By Orev at 2025-06-07 10:05:16:

Is there any reason you’re not using docstrings for this? They’re defined in PEP 257 and seems like the standard way to accomplish what you’re doing. I would expect standard tools like type checkers would work with them.

Aren't you using argparse? I think it has built-in support for the subcommand handling you describe. See this part of its tutorial for how to configure it to call a function corresponding to the positional argument passed: https://docs.python.org/3/library/argparse.html#sub-commands

Written on 06 June 2025.
« You could automate (some) boilerplate Go error handling with a formatter
A silly systemd wish for moving new processes around systemd units »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Fri Jun 6 22:05:01 2025
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.