<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx" -->

---
title: Recording calls with wrapture | daily.dev
description: A deep-dive into wrapture&#x27;s recording model, the timeline and tape, used to catch a resource-leak bug that no return value would reveal. Walks through binding...
canonical: https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Recording calls with wrapture | daily.dev
og:description: A deep-dive into wrapture&#x27;s recording model, the timeline and tape, used to catch a resource-leak bug that no return value would reveal. Walks through binding...
og:url: https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx
og:image: https://api.daily.dev/og/posts/HkPB9GqVX.png
og:image:alt: Recording calls with wrapture
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Recording calls with wrapture

**[Planet Python](https://daily.dev/sources/planetpython)** · 13 min read · 0 upvotes · 0 comments

## Summary

A deep-dive into wrapture's recording model, the timeline and tape, used to catch a resource-leak bug that no return value would reveal. Walks through binding methods to record real calls (including objects minted mid-call like connections), reading events with fields such as path, instance, arguments and result, filtering and asserting on that record, using tape.children_of() and stack capture to name the exact leak site, asserting order across bindings, scoping timelines instead of resetting mocks, and capturing logs and named blocks as events. Ends with a full pytest example that fails on the leak and passes once a finally clause is added.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://grahamdumpleton.me/posts/2026/09/recording-calls-with-wrapture>

## Questions this post answers

### How can I test that a Python function releases a resource on every code path, not just the happy path?

Record real calls to the acquire and release methods on a shared tape, then compare the set of acquired objects against the set of released objects; the difference is the leak. Binding the release method on the class (not an instance) covers objects like connections that are minted mid-call by a factory, which a hand-written fake or a mock injected through a seam cannot see.

_Developers hunting resource leaks in Python code share testing patterns like this on daily.dev._

### How do I find exactly which line acquired a resource that was never released?

Bind the acquire call with stack capture enabled (for example stack="caller"), which records the calling frame with each event. After comparing acquired against released objects, look up the unreleased ones' stack frames to get the exact line number and function where the leak occurred, without needing to know in advance which method was responsible.

_Debugging elusive leaks pushes engineers to compare tracing and recording techniques on daily.dev._

### How is asserting call order across multiple mocked objects different from mock's assert_has_calls?

A cross-binding order check like assert_order(bindingA, bindingB) verifies a subsequence across any number of separately bound methods, allowing other events before, between, and after, unlike assert_has_calls which only checks calls within a single mock. Passing consecutive=True requires no interleaving events from those bindings, and exact=True requires the named events to be the complete set, matching mock_calls == behavior but across bindings.

_Teams weighing mocking strategies for multi-object interactions compare approaches like this on daily.dev._

## Similar posts on daily.dev

- [Live tracing with wrapture](https://daily.dev/posts/live-tracing-with-wrapture-6xkqkaede) · Planet Python · 0 upvotes · 0 comments
- [Beyond callables in wrapture](https://daily.dev/posts/beyond-callables-in-wrapture-a41yd6lmi) · Planet Python · 0 upvotes · 0 comments
- [Unit testing with wrapture](https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61) · Planet Python · 0 upvotes · 0 comments
- [Introducing wrapture](https://daily.dev/posts/introducing-wrapture-ohj3cvcws) · Planet Python · 1 upvotes · 1 comments
- [Zero-code tracing with wrapture](https://daily.dev/posts/zero-code-tracing-with-wrapture-yy6qgb3me) · Planet Python · 1 upvotes · 0 comments

---

Tags: [#python](https://daily.dev/tags/python), [#testing](https://daily.dev/tags/testing)

[View this post on daily.dev](https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Recording calls with wrapture","url":"https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx"},"datePublished":"2026-09-01T21:52:15.672Z","dateModified":"2026-09-01T21:52:43.763Z","description":"A deep-dive into wrapture's recording model, the timeline and tape, used to catch a resource-leak bug that no return value would reveal. Walks through binding...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/1241c05778128b665d351eff8322d6e7?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/1241c05778128b665d351eff8322d6e7?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Planet Python","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Planet Python","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/eac3cc584cb043b5a242d13752bd3d0c","url":"https://daily.dev/sources/planetpython"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"python,testing","timeRequired":"PT13M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Planet Python","item":"https://daily.dev/sources/planetpython"},{"@type":"ListItem","position":3,"name":"Recording calls with wrapture"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx#faq","mainEntity":[{"@type":"Question","name":"How can I test that a Python function releases a resource on every code path, not just the happy path?","acceptedAnswer":{"@type":"Answer","text":"Record real calls to the acquire and release methods on a shared tape, then compare the set of acquired objects against the set of released objects; the difference is the leak. Binding the release method on the class (not an instance) covers objects like connections that are minted mid-call by a factory, which a hand-written fake or a mock injected through a seam cannot see. Developers hunting resource leaks in Python code share testing patterns like this on daily.dev."}},{"@type":"Question","name":"How do I find exactly which line acquired a resource that was never released?","acceptedAnswer":{"@type":"Answer","text":"Bind the acquire call with stack capture enabled (for example stack=\"caller\"), which records the calling frame with each event. After comparing acquired against released objects, look up the unreleased ones' stack frames to get the exact line number and function where the leak occurred, without needing to know in advance which method was responsible. Debugging elusive leaks pushes engineers to compare tracing and recording techniques on daily.dev."}},{"@type":"Question","name":"How is asserting call order across multiple mocked objects different from mock's assert_has_calls?","acceptedAnswer":{"@type":"Answer","text":"A cross-binding order check like assert_order(bindingA, bindingB) verifies a subsequence across any number of separately bound methods, allowing other events before, between, and after, unlike assert_has_calls which only checks calls within a single mock. Passing consecutive=True requires no interleaving events from those bindings, and exact=True requires the named events to be the complete set, matching mock_calls == behavior but across bindings. Teams weighing mocking strategies for multi-object interactions compare approaches like this on daily.dev."}}]}
```

