Context Managers and Regular Expressions
Safe resources, and powerful pattern matching.
You have used with open, many times, and it always cleaned up after itself. Ava wants the same safety for her own resources, like a timer, or a database. And she also needs to find phone numbers and emails buried in customer messages. For the first, we use context managers. For the second, regular expressions.

- with: set up, and always clean up
- Build your own context manager
- Search text by pattern
Writing a context manager
A context manager has two special methods. Enter runs when the with block begins, and exit runs when it ends, even if an error occurs. Here we print messages to see the order. This guarantees cleanup, such as closing a file, or releasing a lock.
class Shop:
def __enter__(self):
print("Shop opens")
return self
def __exit__(self, exc_type, exc, tb):
print("Shop closes")
with Shop() as s:
print("Serving customers")
print("Done")Shop opens Serving customers Shop closes Done
contextlib: the easy way
The contextlib module makes it shorter. Decorate a generator with context manager. The code before yield is the setup, and the code after it is the cleanup. Using try and finally guarantees the cleanup happens, even after an error. Here, a timer block reports when it finishes.
from contextlib import contextmanager
@contextmanager
def opened(name):
print("open", name)
try:
yield name
finally:
print("close", name)
with opened("orders") as n:
print("working with", n)open orders working with orders close orders
Regular expressions
A regular expression is a pattern for text. The re module searches with it. Backslash d means a digit, and plus means one or more. Search finds the first match. Findall returns all of them. Sub replaces matches. And brackets capture parts. Use raw strings, with an r before the quote, so backslashes are left alone.
Regex cheat sheet
A few pieces cover most needs. Dot matches any character. Backslash d is a digit, w a word character, s a space. Star means zero or more, plus means one or more, question mark means optional. Curly brackets set exact counts. Square brackets list allowed characters. Caret and dollar anchor the start and end. Build patterns slowly, and test as you go.
| Pattern | Meaning | Example match |
|---|---|---|
| \d | a digit | 7 |
| \w+ | a word | plant |
| [abc] | one of a, b, c | b |
| a{2} | exactly two a | aa |
| ^ and $ | start and end | whole line |
| (…) | capture a group | the part you need |
Parsing with groups
Groups pull structured data out of text. Here, Ava's order messages follow a pattern, a quantity, and a plant name. The pattern captures both. Match returns an object, and group gives each captured part. Named groups make the code self explaining. Regex is powerful, but keep patterns short and well tested.
import re
msg = "order: 3 x monstera"
m = re.match(r"order: (\d+) x (\w+)", msg)
print(m.group(1), m.group(2))
m2 = re.search(r"(?P<qty>\d+) x (?P<name>\w+)", msg)
print(m2.groupdict())3 monstera
{'qty': '3', 'name': 'monstera'}Find the emails
Your turn. Use findall with a regular expression, to extract the email addresses from the text, and print them. Ready? Here is one solution. A pattern with word characters on both sides of an at sign. Real email patterns are longer, but this one works for simple cases.
Use re.findall to extract all emails from the message and print them.
Show hint
pattern: [\w.]+@[\w.]+
Show solution
import re
text = "Write to ava@leafy.com or sam@shop.org today"
print(re.findall(r"[\w.]+@[\w.]+", text))['ava@leafy.com', 'sam@shop.org']
Summary
Great work. You can build context managers with enter and exit, or with contextlib, and you can find and extract text with regular expressions. That completes the Pythonic toolbox. Next, we move towards professional habits, starting with type hints, and checking your code.
Key takeaways- with = setup + guaranteed cleanup
- @contextmanager makes it short
- re: search, findall, sub, groups