Provyn Prep · 8 min read

How to pass the Provyn Python assessment

Sixty minutes, fifteen questions. The assessment targets Python 3.10+ and tests you on the standard library, idiomatic patterns, and real engineering trade-offs — not trivia.

What you will be tested on

  • List/dict/set comprehensions and generator expressions
  • Decorators and context managers
  • Threading vs asyncio vs multiprocessing trade-offs
  • Type hints: generics, TypeVar, Protocol, Literal
  • Standard library: itertools, functools, collections, pathlib
  • Error handling: exception hierarchy, custom exceptions

What the assessment is testing

The Python assessment is designed around the kind of code you would write in a backend service or data pipeline. Questions are scenario-based: given this requirement, which approach is correct and why? MCQ questions test conceptual understanding; code questions test whether you can actually write idiomatic Python.

The heaviest-weighted area is concurrency — specifically knowing when to reach for threading, asyncio, or multiprocessing and why the wrong choice either deadlocks, underperforms, or doesn't help.

Concurrency: the highest-value topic

Python's GIL means threads don't help with CPU-bound work. Use multiprocessing for CPU-bound parallelism and threading or asyncio for I/O-bound work. The assessment will give you a scenario ("you need to fetch 1000 URLs as fast as possible") and ask which concurrency model is correct.

asyncio questions focus on async def, await, asyncio.gather, and asyncio.create_task. Know the difference between gather (waits for all) and create_task (fires and forgets, must be awaited later). Know that asyncio is single-threaded — blocking calls inside an async function block the entire event loop.

Decorators and context managers

At least one question will ask you to write a decorator from scratch. The wrapper must use functools.wraps or the decorated function loses its __name__ and __doc__. Know the pattern: def decorator(fn): @functools.wraps(fn) def wrapper(*args, **kwargs): ... return fn(*args, **kwargs) return wrapper.

Context managers: know both the __enter__/__exit__ class form and the @contextmanager generator form. The generator form uses yield inside a try/finally — what goes before yield is the __enter__ body, what goes after is __exit__.

Three-day prep plan

Day one: run the practice assessment. Note which topics caused hesitation.

Day two: drill itertools.chain, groupby, product, and combinations in a REPL. These appear on almost every attempt. groupby is particularly tricky because the input must be sorted by the grouping key or you get unexpected behaviour.

Day three: write two decorator implementations from scratch: one that retries on exception (with configurable max_retries) and one that caches the result (memoize). Both patterns appear in graded questions.

Point-losing mistakes

Mutable default arguments. def func(x, items=[]): is a classic Python trap. The list is shared across all calls. The assessment will test this with a scenario where the bug produces unexpected output.

Using is for value comparison. is checks object identity, not equality. "hello" is "hello" might return True due to interning, but it's not guaranteed and is wrong idiomatically. Use == for values.

Missing finally in resource-handling code. Questions that show a file open without a with statement and ask what happens on exception — the file is not closed. The correct answer always involves with or finally.

Ready when you are

Take the Python assessment

Sixty minutes. One credential. Free tier — no card required.

Last updated 2026-05-01.