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.

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.wraps in decorators.
  • ImportError: ensure the package has __init__.py or 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.