Adam Johnson

https://adamj.eu/tech

41 posts

Tech

Subscribe via RSS

  1. Python: inspect unittest.mock calls with attached mocks

    When you use a unittest.mock mock, you often make assertions on the calls it received, such as through the mock_calls list. But sometimes you want to assert on the order of calls across multiple mocked functions, for example to check that steps in a process happen in the right sequence. To do this, use attached mocks, which let you collect calls from multiple mocks into a single timeline. A parent mock has multiple child mocks attached to it, and its mock_calls list records calls from all of…

    0 ▲
  2. Python: spy on function calls with unittest.mock’s wraps

    Testing terminology distinguishes between different kinds of test doubles, including: mocks, which replace the real behaviour of a function. spies, which wrap a function, recording calls for later assertions. Despite its name, unittest.mock is not just for mocks: it can also create spies, through the wraps argument of its Mock classes. Calls to such a Mock pass through to the wrapped object and return its real results, while the mock records the calls for later assertions. For example, say we…

    0 ▲
  3. Django: release code words up to 6.1

    Did you know that each Django release has a “code word” associated with it? It’s hidden in plain sight, in the announcement blog post describing the list of features coming in the next version. I think this is a lovely little tradition. I last covered the list back in 2021, for Django 3.2 (post). This post expands the table up until Django 6.1, which is expected next month (the first release candidate came out earlier this week). Each code word links to its Wiktionary entry so you can see the…

    0 ▲
  4. Django: introducing django-crawl

    I recently migrated one of my client projects from the legacy django-csp package to Django 6.0’s built-in Content Security Policy (CSP) support (release note). This security header is a powerful tool for preventing unwanted content from being loaded on your site, so configuration correctness is paramount. The migration was fairly straightforward, but a few pages had complicated overrides, so I wanted to be sure that no CSP headers had been changed by my swapping of CSP implementations. I had…

    0 ▲
  5. Django: introducing django-orjson

    Just as cars painted red are known to be faster, libraries implemented in Rust are also known to be faster. Today’s example is orjson, a Rusty replacement for Python’s built-in json module, boasting 10x faster serialization and 2x faster deserialization. Such a library is great, but adopting it isn’t easy, especially when your framework uses json in many different parts. To help Django developers adopt orjson, I have created django-orjson, which provides a whole bunch of drop-in replacements…

    0 ▲
  6. Python: find all instances of a class with gc.get_objects()

    Here’s a lovely hack that I’ve used when machete-mode debugging. Say you have a class, and you want to find its live instances, perhaps to check how many there are or what value a certain attribute has across them. Normally you'd have to alter the class initialization to store its instances in a collection, like a WeakSet, but making such edits slows down debugging and isn’t always possible. The approach in this post uses the garbage collector module, gc, to leverage its already-tracked…

    0 ▲
  7. Python: store extra data for objects in a WeakKeyDictionary

    In several programs, I’ve wanted to solve the problem of associating extra data with an object. For example, in django-upgrade, the individual “fixer” functions often want to store extra data per visited ast.Module object. A common pattern in Python is to store the data in an extra attribute directly on the object, like module._all_used_names = .... However, this approach has some downsides: The object may not allow arbitrary attributes, such as for built-in types like dict or slotted classes.…

    0 ▲
  8. Django: introducing django-integrity-policy

    Back in January, Firefox’s Security & Privacy Newsletter for 2025 Q4 piqued my interest with this mention: Integrity-Policy: Firefox 145 has added support for the Integrity-Policy response header. The header allows websites to ensure that only scripts with an integrity attribute will load. A new security header! That’s right up my street: I’ve cared about getting security headers right since 2018, when I created django-permissions-policy to set the Permissions-Policy header. (At the time, it…

    0 ▲
  9. Django: fixing a memory “leak” from Python 3.14’s incremental garbage collection

    Back in February, I encountered an out-of-memory error while migrating a client project to Python 3.14. The issue occurred when running Django’s database migration command (migrate) on a limited-resource server, and seemed to be caused by the new incremental garbage collection algorithm in Python 3.14. At the time, I wrote a workaround and started on this blog post, but other tasks took priority and I never got around to finishing it. But four days ago, Hugo van Kemenade, the Python 3.14…

    0 ▲
  10. Python: introducing profiling-explorer

    I’ve made another package! Like icu4py, which I made in February, it was sponsored by my client Rippling. And like tprof, which I made in January, it’s a profiling tool! profiling-explorer is a tool for exploring profiling data from Python’s built-in profilers, which are stored in pstats …

    0 ▲
  11. Python: introducing icu4py, bindings to the Unicode ICU library

    I made a new package! Thank you to my client Rippling for inspiring and sponsoring its development. ICU (International Components for Unicode) is Unicode’s official library for Unicode and Globalization tools. It’s a de-facto standard for handling text in a locale-aware way, used by many major projects, including …

    0 ▲