<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/when-try-finally-becomes-a-code-smell-and-the-fix--wripulrl6" -->

---
title: When try/finally Becomes a Code Smell (And the Fix)
description: Nested try/finally blocks for resource management are a code smell in Python. The with statement and context manager protocol (__enter__/__exit__) provide a...
canonical: https://daily.dev/posts/when-try-finally-becomes-a-code-smell-and-the-fix--wripulrl6
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: When try/finally Becomes a Code Smell (And the Fix) | daily.dev
og:description: Nested try/finally blocks for resource management are a code smell in Python. The with statement and context manager protocol (__enter__/__exit__) provide a...
og:url: https://daily.dev/posts/when-try-finally-becomes-a-code-smell-and-the-fix--wripulrl6
og:image: https://api.daily.dev/og/posts/WrIpuLRl6.png
og:image:alt: When try/finally Becomes a Code Smell (And the Fix)
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.

# When try/finally Becomes a Code Smell (And the Fix)

**[Medium](https://daily.dev/sources/medium_js)** · 10 min read · 0 upvotes · 0 comments

## Summary

Nested try/finally blocks for resource management are a code smell in Python. The with statement and context manager protocol (__enter__/__exit__) provide a composable, reusable alternative. Three patterns are covered: @contextmanager decorator for stateless managers, class-based __enter__/__exit__ for stateful ones, and ExitStack for managing N dynamic resources. Practical examples include database connections, thread locks, temp directories, HTTP sessions, and test mocks. Key gotchas include the exception suppression trap (returning True from __exit__), exceptions in __exit__ silently replacing the original exception, and the requirement that @contextmanager generators yield exactly once.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://blog.devgenius.io/when-try-finally-becomes-a-code-smell-and-the-fix-d4bbe9c9ac99>

## Questions this post answers

### Why does my custom __exit__ method swallow the original exception when it raises a new one during cleanup?

Raising a new exception inside __exit__ silently replaces the original exception propagating out of the with block, so the caller only sees the cleanup error and the original error (for example a ValueError) disappears. The fix is to wrap cleanup code in its own try/except inside __exit__, log the cleanup failure, and return False so the original exception still propagates.

_Track down subtle exception-swallowing bugs like this one by following Python deep dives on daily.dev._

### Should I use contextlib.contextmanager or write a class with __enter__ and __exit__ for a Python context manager?

Use @contextmanager as the default for stateless resource management since it's shorter and everything before yield is __enter__ while everything after is __exit__, wrapped in try/finally. Write a class-based context manager instead when you need state to persist after the with block (like a timer's elapsed time), reuse via subclassing, or constructor parameters stored as instance variables.

_Compare Python resource-management patterns like these on daily.dev before picking one for your codebase._

### How do I clean up a dynamic number of resources in Python without deeply nesting with statements?

Use contextlib.ExitStack, which lets you call stack.enter_context() in a loop to register any number of resources (files, locks, patches) and guarantees they are closed in reverse (LIFO) order when the stack exits, even if an exception occurs. ExitStack also supports conditional registration and stack.pop_all() to transfer cleanup ownership to a caller.

_Find patterns like ExitStack for managing multiple resources cleanly via daily.dev._

## Similar posts on daily.dev

- [Context Managers and Using Python's with Statement Quiz – Real Python](https://daily.dev/posts/context-managers-and-using-python-s-with-statement-quiz-real-python-n7vosulnn) · Real Python · 0 upvotes · 0 comments
- [Understand Error Handling Best Practices in Python](https://daily.dev/posts/understand-error-handling-best-practices-in-python-d6ond615u) · Medium · 1 upvotes · 0 comments
- [.NET Developer, Personal Blog](https://daily.dev/posts/net-developer-personal-blog-zxaphss3e) · Laszlo Deak · 13 upvotes · 0 comments

---

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

[View this post on daily.dev](https://daily.dev/posts/when-try-finally-becomes-a-code-smell-and-the-fix--wripulrl6)

```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":"When try/finally Becomes a Code Smell (And the Fix)","url":"https://daily.dev/posts/when-try-finally-becomes-a-code-smell-and-the-fix--wripulrl6","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/when-try-finally-becomes-a-code-smell-and-the-fix--wripulrl6"},"datePublished":"2026-07-22T20:12:03.208Z","dateModified":"2026-09-14T07:50:57.914Z","description":"Nested try/finally blocks for resource management are a code smell in Python. The with statement and context manager protocol (__enter__/__exit__) provide a...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/b0d9cfd454ef38dc6fb7832b248f60ce?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/b0d9cfd454ef38dc6fb7832b248f60ce?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Medium","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":"Medium","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/medium","url":"https://daily.dev/sources/medium_js"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/when-try-finally-becomes-a-code-smell-and-the-fix--wripulrl6","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"python","timeRequired":"PT10M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Medium","item":"https://daily.dev/sources/medium_js"},{"@type":"ListItem","position":3,"name":"When try/finally Becomes a Code Smell (And the Fix)"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/when-try-finally-becomes-a-code-smell-and-the-fix--wripulrl6#faq","mainEntity":[{"@type":"Question","name":"Why does my custom __exit__ method swallow the original exception when it raises a new one during cleanup?","acceptedAnswer":{"@type":"Answer","text":"Raising a new exception inside __exit__ silently replaces the original exception propagating out of the with block, so the caller only sees the cleanup error and the original error (for example a ValueError) disappears. The fix is to wrap cleanup code in its own try/except inside __exit__, log the cleanup failure, and return False so the original exception still propagates. Track down subtle exception-swallowing bugs like this one by following Python deep dives on daily.dev."}},{"@type":"Question","name":"Should I use contextlib.contextmanager or write a class with __enter__ and __exit__ for a Python context manager?","acceptedAnswer":{"@type":"Answer","text":"Use @contextmanager as the default for stateless resource management since it's shorter and everything before yield is __enter__ while everything after is __exit__, wrapped in try/finally. Write a class-based context manager instead when you need state to persist after the with block (like a timer's elapsed time), reuse via subclassing, or constructor parameters stored as instance variables. Compare Python resource-management patterns like these on daily.dev before picking one for your codebase."}},{"@type":"Question","name":"How do I clean up a dynamic number of resources in Python without deeply nesting with statements?","acceptedAnswer":{"@type":"Answer","text":"Use contextlib.ExitStack, which lets you call stack.enter_context() in a loop to register any number of resources (files, locks, patches) and guarantees they are closed in reverse (LIFO) order when the stack exits, even if an exception occurs. ExitStack also supports conditional registration and stack.pop_all() to transfer cleanup ownership to a caller. Find patterns like ExitStack for managing multiple resources cleanly via daily.dev."}}]}
```

