function decorators in python

26 Sep 2022

Function decorators are an elegant way of wrapping a function inside another function. I’m posting this here so I can always look it up but there’s good stuff on youTube too.

Suppose you want to do something every time a function is called, log it’s arguments and return value perhaps, or test how fast it runs, or cache it’s results. Decorators are your friends here.

# wrapper function
def log(fn):
    def wrapper(*args, **kwargs):
        print(fn)
        out = fn(*args, **kwargs)
        print("\tin: {}\n\tout: {}\n".format((*args,) + (kwargs,), out))
        return out
    return wrapper

# a different wrapper function
def timeThis(fn):
    def wrapper(*args, **kwargs):
        start = timer()
        out = fn(*args, **kwargs)
        end = timer()
        print("execution took: {:0.8f} seconds".format(end - start))
        return out
    return wrapper

# 'decorate' the function with the name of the decorator
@log
def add(x, y=1):
    return x+y

@timeThis
def sumOfSquaresUpTo(x):
    return sum([x**2 for x in range(1,x+1)])

add(5, y=4)
sumOfSquaresUpTo(200)

The key here is that the function you call, add for example, is actually fed into log first, log returns the wrapper function that calls add when it is itself called.


virtual environments in python

11 Apr 2022

This gets added to the very long list of programming techniques I wish I’d got into earlier (git, unit testing, understanding debuggers… ad inf)

Python is my go to language for prototyping my ideas, or, putting less of a old-wise-man spin on it: Python is what I reach for when I think “This should be easy”, before realising it’s a big project and probably would have benefitted from a different approach.

Virtual environments: You usually end up installing loads of python dependencies via pip which you only use for that one project. This is where virtual environments come in. A virtual environment lives in your working folder and holds all the pip packages you’re using just for this project.

create a new virtual environment (henceforth venv) like This:

python3 -m venv [the name of your venv (usually I call it venv)]

That’s assuming you want to be using python 3.

then activate the venv:

source ./venv/bin/activate

At this point you may see ‘venv’ appear on your command prompt depending on what you’re using. OhMyZsh will display it along with other nice things like git status.

Now whenever you install anything via pip it will exists only in your local venv and not clutter or mess up anything else on your system. Also when you run python it will be the version of python you invoked when you created the venv, so you don’t need to keep typing python3, just type python instead.

To deactivate the venv just type

deactivate

Then cd to another python project and activate the venv for that one and you can pick up where you left off with that project running a completely different set of dependencies.

Git and Venvs

Probably you don’t want to add your venv folder to git (or push it to a remote). So add venv to .gitignore

echo venv >> .gitignore

and run

pip freeze > requirements.txt

And add this file to your git repo so you never loose track of what pip packages are needed.


for else in python

10 Mar 2021

This was new to me! While putting together a Python course on make things I ended up with a piece of code that didn’t behave as expected. It turned out I’d incorrectly indented an else inside a for loop accidentially creating a for-else loop:

for number in range(22,25):
  if number % 7 == 0:
    print(f"found a multiple of 7: {number}")
    break
else:
  print("didn't find a multiple of 7")

You need the break statement, otherwise the else clause is executed after the for loop finishes.


ssh copy id

28 Dec 2020

I really came late to the joys of ssh.

During lockdown I’ve been messing about with a raspberry pi I’ve had sitting around for ages. Normally my ssh routine is like this:

ls ~/.ssh

you should see something like

id_rsa
id_rsa.pub
known_hosts

then paste the contents of id_rsa.pub which is your public key, into the ~/.ssh/authorised_keys folder on the remote machine. From then you should be able to log in without giving your password just by typing

ssh [name]@[remote machine]

But there’s a much nicer way:

ssh-copy-id [remote user name]@[remote machine]

so for example I typed from my machine

ssh-copy-id pi@192.168.1.117

the raspberry pi asks for your password and you’re done.

Elegant stuff!

This should also work for rsyncing my webspace, but I set that up the old way.


making qr codes for jekyll pages

28 Dec 2020

While building hand made science I found myself making lots of printable things, especially lab sheets for practical work, question pages etc.

I thought it would be cool if you had a QR code on the question sheet which takes you to the page with the video and other blurb.

After googleling about for a bit and not finding a nice way to get Jekyll to run a script every time you build your site, I ended up making my own solution. I suspect this is massively clunky if you’re a Jekyll pro.

I added a variable to the front matter of pages I wanted QR codes for:

makeQRCode: true

Then I added this to my default template, so that it’s available on all pages.

1
2
3
4
5
<!-- 
{% if page.makeQRCode %}
_QR-"{{page.title}}".png  "https://www.handmadescience.co.uk{{page.url}}"
{% endif %}
-->

Line 3 gives me two useful things, a filename for the QR code, and the URL the QR needs to point to.

lines 1 and 5 are HTML comments that stop the text on line 3 from showing up on the site. Yes I could have deleted the line when I’m finished, that would have been nicer.

I added the underscore at the start of line 3 so Jekyll ignores the generated png files, otherwise they get added to the built _site folder.

Then I added the following script in QRMaker.sh:

#!/bin/zsh
cat _site/**/*.html | grep ^_QR- | xargs -n 2 qrencode -o 

Which gets called whenever I run my upload script.

the ** tells zsh to look in subfolders to any depth for html files, the ^ tells grep to only look for lines starting with the pattern. I also found I had to tell xargs to expect 2 arguments, hence -n 2

Only pages on which I’ve set makeQRCode: true will contain the line starting with "_QR-".

Finally It uses xargs to feed those two strings into the qrencode utility which will spit out a png for the relevant URL.

For completeness the upload script is:

#!/bin/zsh
JEKYLL_ENV=production bundle exec jekyll build && 
rsync -a _site/ [user]@[host]:handmadescience/
source QRMaker.sh

Qrencode can be downloaded easily, I’m on a mac so

brew install qrencode

Works fine.