Python Functions, Decorators, and Reusable Modules
I refactored a monolithic script into well-scoped functions, a reusable importable module, and decorators for cross-cutting concerns like timing and retry. Modular structure made the code unit-testable and reusable across multiple automation tasks.
Objective & Context
Functions and modules are how Python code stays DRY and testable. This lab covers signatures, *args/**kwargs, decorators with functools.wraps, and the import system, turning one-off scripts into a maintainable package.
Environment & Prerequisites
- Python 3.11 with functools.
- An existing script to refactor.
- pytest for verifying behaviour.
flowchart LR
M[monolith.py] --> P[package/]
P --> C[core.py functions]
P --> D[decorators.py]
P --> T[tests/]
Step-by-Step Execution
1. A retry decorator with wraps
import functools, time
def retry(n=3):
def deco(fn):
@functools.wraps(fn)
def wrap(*a, **k):
for i in range(n):
try: return fn(*a, **k)
except Exception:
if i == n-1: raise
time.sleep(2**i)
return wrap
return deco
2. Import the module
python -c "from mypkg.core import process; print(process('x'))"3. Run the tests
pytest -q tests/..... 5 passed in 0.12s
Validation & Testing
Unit-test each function in isolation and confirm the retry decorator backs off and re-raises after the limit. Pass criteria: green pytest run, importable package, and decorators preserve function metadata via wraps.
Advanced: Troubleshooting
- Lost docstring/name: always apply
functools.wrapsin decorators. - ImportError: ensure the package has
__init__.pyor is on the path. - Hidden state: avoid mutable module-level globals in reusable code.
Key Results
- Refactored a monolith into a tested, importable package.
- Added retry/timing as reusable decorators with preserved metadata.
- Reached a green unit-test suite covering core functions.
- Enabled reuse of the module across multiple automation scripts.