Use the included batteries


It's been a while since I last updated this blog. Starting a new (now not so new job) and having two kids does tend to make you prioritise things differently. I have also found with the current job it gives me more than enough of a chance to work on interesting/challenging projects, I feel much less need to code at home. In fact most of my hobby time is probably taken up with drawing (which you can see over on my instagram account).

Anyway, I've finally thought I'd write down some of the things I do when writing "simple" Python scripts. There are often many times where I need a script of some sort for a job at work (and occasionally home). As with most people this probably starts off with a few lines of shell script, but once complex logic is involved I tend to reach for Python. However I also want to maintain the relative simplicity and ease of distribution that a simple shell script offers. It's not uncommon that a co-worker might find a script useful. If the "installation" step it just "drop it in your ~/bin directoy" then it's easier all round. It also means I'm more likely to share the script if I know there will be less debugging of my co-workers Python setup!

For these sorts of scripts (maybe up to a few hundred lines of code or so) I roughly follow this philosophy:

  • use the built-in libraries only
  • keep it all in one file
  • always use argparse

Use the built-in libraries only

Python is well known for having "Batteries Included". It's core modules cover quite a lot of functionality. A lot of the time I find that I just need to run some sub-processes, talk to an API of some sort (typically a JSON dialect) and do a bit of logic.

It's not always possible to not use a third party library though. You might need to talk to a Postgres db or something. Though even then if it's just a dev-script you can maybe get away with calling psql via the subprocess library.

The main reason to avoid 3rd party libraries is that it makes installation a breeze. No messing around with virtual environments or docker etc. Those do have their place (in production they are essential), but for a simple script it can be overkill. Particularly if you are just installing a library to make things "nicer" for development.

Keep it all in one file

Again this just helps make it easy to install the script. It just needs to be marked as executable and put in the right place. To avoid surprises I also tend to add:

#!/usr/bin/env python3

Just in case you are running on a system where Python 2 is still the default (which it often is for various legacy reasons)

Always use argparse

Using argparse means you get things like --help flags for free. If you are sharing your scripts at all this can make a huge difference to usability. This is also handy for yourself when you can't quite remember how to run your own script - which happens quite often I find. It will also encourage you not to do things like hardcode the address of your dev instance - once you've done the initial setup for argparse adding new arguments is trivial.

Bonus tip

One thing I regularly do is add a "verbose" option to my scripts. This is handy for debugging etc - you can keep the script normally quiet, but then dial up the output when you need more detail. In a "proper" program you would use a proper form of logging, but this little trick is perfect for small scripts that exist in a single file (it won't really work outside of a single file):

parser = argparse.ArgumentParser()
parser.add_argument('--verbose', action='store_true')
# other args etc

args = parser.parse_args()

if args.verbose:
    verbose = print
else:
    def verbose(*arg, **kw):
        pass

# then call verbose() like you would call print() and it will
# by default not print anything unless you provide the --verboase
# flag when you run the script

Conclusion

This approach doesn't scale well, but for scripts of a certain size they can make life a lot easier. Particulary for the kind of scripts that a typical dev might want or need.