I have created or maintain a number of open source projects, this is a list of the most noteworthy.

"" Actively Maintained ""



Wrap a WSGI application in an AWS Lambda handler function for running on API Gateway or an ALB.

I wrote this while working at Time Out to help build an AWS Lambda based Django application. Existing solutions for the problem, such as Zappa, required complete control over the application deployment, while we already had a defined deployment pipeline using Ansible (roughly like this blog post). Therefore I created this open source layer to translate the AWS API Gateway requests back and forth to WSGI, allowing any standard Python web application to be deployed on Lambda.



Run Black on Python code blocks in documentation files.

Anthony Sottile wrote this project in 2018. I took over its maintainence end of 2022. It’s a neat tool that I use to format code examples in my blog and books. It has the nice side effect of ensuring the examples have valid syntax.



Automatically reload your browser in development.

See the introductory blog post.



Capture and make assertions on transaction.on_commit() callbacks.

Read more in my blog post The Fast Way to Test Django transaction.on_commit() Callbacks.



A Django App that adds Cross-Origin Resource Sharing (CORS) headers to responses.

I took over maintenance of this from the creator Otto Yiu in 2016 and have shepherded it through many releases since. It adds CORS headers and is by far the most used package I maintain.



Extensions for using Django with htmx.

htmx extends HTML to allow you to build complex features without any JavaScript. I co-authored this package with Niccolò Cantù to provide useful extensions to Django for working with htmx.



Ensure your migration history is linear.

An extension to Django’s migrations framework to make migrations easier to handle. See my introductory blog post.



Use `minify-html <>`__, the extremely fast HTML + JS + CSS minifier, with Django.

A middleware to minify your HTML. See my introductory tweet thread.



Django-MySQL is a non-inventively named package that helps you use some MySQL/MariaDB-specific features in the world of Django.

This is my biggest self-made project, and what gained me the most attention from the Django community for my invitation as a core contributor.



Keep detailed records of the performance of your Django code.

This was based off some code I helped develop at YPlan. I was given permission to open source it, which I did at the PyCon UK 2017 sprints, although with some heavy re-factoring.



Set the draft security HTTP header Permissions-Policy on your Django app.

I created this at Genus AI while working to get the web platform to an A+ score on It adds control over the header Permissions-Policy, which at time of writing is still experimental.



Disable Django database writes.

This is based on a technique we had at YPlan, but rewritten to use Django’s database instrumentation. It can be useful to activate it for interactive sessions, so you don't accidentally modfiy your database.



Extensions for using Rich with Django.

Rich provides tools for nice terminal output. This package helps you use Rich within Django.



Automatically upgrade your Django projects.

Automated source code rewriter for upgrading Django projects.



System checks for your project’s environment.

django-version-checks adds several system checks that can help ensure that the current environment has the right versions of Python, databases, etc. This is useful when coordinating upgrades across all your infrastructure. See my introductory blog post.



An easy interface to query the EC2 metadata API, with caching.

I wrote this while at Time Out to fill in a gap where AWS' library boto provided an interface to the EC2 Metadata Service while the new version boto3 doesn't.



A flake8 plugin that helps you write better list/set/dict comprehensions.

I was given permission to write this as a plugin at YPlan through our "Friday Afternoon Time" initiative to find and fix some common performance-impacting comprehension issues we found across the YPlan codebase.



A flake8 plugin to ban PEP-420 implicit namespace packages. *

Implicit namespace packages are folders containing Python files but without an file. They’re valid in Python since PEP-420, but many code quality tools don’t discover them - at least, Django’s test runner, and Mypy without --namespace-packages. After hitting such problems several times I wrote this plugin to prevent implicit namespace packages on projects that don’t need them.



A flake8 plugin that helps you write tidier imports.

Similar to the above, this was also created at YPlan to improve code quality, and later to ban some imports when migrating to Python 3.



Use heroicons in your Django and Jinja templates.

A Python package for using this great icon set. See my blog post introducing it.



Patch the inner source of python functions at runtime.

This is a real hack, but we used it at YPlan to modify just a few of Django's innards without having to fork it. I gave a lightning talk on it at PyCon UK 2015. Since publishing, my ex-colleague Tom Grainger has fixed a lot of its edge cases.



Pygments lexers for Git output and files.

I created this to nicely highlight examples in my writings about Git.



A pytest fixture for testing flake8 plugins.

Used for testing my flake8 plugins such as flake8-comprehensions. The successor to pytest-flake8dir, using pathlib.Path instead of the old py lib py.path class, following pytest’s example.



pytest plugin providing a function to check if pytest is running.

Useful for doing stuff only if pytest is/is not running.



pytest plugin to randomly order tests and control random.seed.

I wrote this plugin at YPlan to improve the test suite and was given permission to open source it. I've written a blog post covering its history.



pytest plugin to restrict the test types allowed.

A pretty niche pytest plugin, we used this to defend the YPlan test suite against non-internal TestCase classes being inadvertently used in tests, which could break assumptions from our other testing tools.



pytest plugin to reverse test order.

A small plugin to reverse test order. Inspired by a user issue on pytest-randomly that pointed to a paper suggesting reversal is almost as effective as random ordering for discovering non-isolated tests.



Travel through time in your tests.

A library for mocking the current date and time in tests. See my blog post introducing it.



Barcode rendering for Python.

My colleagues Christian Muirhead and Julius Seporaitis created this package when we worked together at YPlan. I have maintained it since because I think it’s pretty useful. It wraps a neat tool called BWIPP (Barcode Writer in Pure Postscript).



Parametrize tests within unittest TestCases.

I created this to provide an API like pytest’s parametrization decorator for unittest test cases.

💀 No Longer Maintained 💀



Let Django use settings from an arbitrary Python file instead of an importable module.

I wrote this as an investigation based on an idea posted by Jamesie Pic on the django-developers mailing list. It was useful as a maintenance "canary," for example when testing new Django versions, but it was never popular.



A flake8 plugin to ban type hints.

I wrote this while working on a codebase with type hints but no MyPy setup, which meant many of the hints had bitrotted to be fairly wrong. I figured it would be better to have no hints rather than wrong hints. This isn’t really true though, as many python libraries, like dataclasses, require type hints to function. It’s better to run Mypy.



A decorator to make a function accept keyword arguments only, on both Python 2 and 3.

This was useful for making functions more robust for callers by not accepting positional arguments. For example, it's used in the timezonefinder library to avoid users mixing the 'longitude' and 'latitude' arguments, which have varying conventions on order. I no longer maintain it because it's only needed on Python 2, because Python 3 has native syntax (def func(*, foo=1)).



Pack/unpack Python dicts into/out of MariaDB’s Dynamic Columns format.

I wrote this during PyCon UK 2016 as a way of supporting this JSON-like data type in Django-MySQL and got great support from David R. MacIver in the sprints testing it with Hypothesis. It was a fun exercise implementing a serializer/deserializer of a binary format in pure Python, optimizing it to run quickly, and discovering many bugs with Hypothesis' property-based testing.

I stopped maintaing it though since it was never popular. The Dynamic Columns data type is a niche MariaDB feature, no longer really developed. JSON is a better cross-DB alternative.



Run multiple python linters easily.

I made this to make my open source projects more consistent, by applying the same linters to the same sets of paths easily. I've stopped maintaining it since I use the much more powerful pre-commit to lint my projects now.



Nose plugin to randomly order tests and control random.seed.

I wrote this plugin at YPlan to improve the test suite and was given permission to open source it. I've written a blog post covering its history and that of its successor pytest-randomly. I stopped maintaining it because Nose itself is no longer maintained. I recommend pytest as an alternative.



A pytest fixture for testing flake8 plugins.

Used for testing my flake8 plugins such as flake8-comprehensions. Succeeded by pytest-flake8-path.



pytest plugin to check your TestCase classes call super in setUp, tearDown, etc.

Another plugin open sourced from YPlan. Django's TestCase does some magic to mean you don't need to call super() in your setUp(), but this can mean forgetting to call it when it's needed inheriting from subclasses of TestCase. This plugin runs a simple check that all setUp() (and related) methods call super(), regardless of whether it's needed. I now think this would be better implemented as a lint rule.

Sound Resynthesis with a Genetic Algorithm


This is the source code for my final year thesis at Imperial College. I haven't tried to run it since 2011 but I put it on GitHub as some readers emailed me asking for it, and the thesis is in the repository too.



Adds the --py flag to tox to run environments matching a given Python interpreter.

A little tox plugin, this made it easy to set up my preferred CI setup. With the release of tox 4 in December 2022, it became redundant, as tox gained an option to do basically the same thing.