What does running CPython with the `-X dev` switch turn on?
answer
- One switch, many extra checks
- Makes a quiet interpreter noisy
- Same bundle available from an environment variable
- The interpreter can report it about itself
- Not a rebuilt interpreter
basics
~20 sDevelopment mode makes a normal interpreter noisy. It adds the default warning filter so ignored categories like ResourceWarning print, installs debug hooks around memory allocations, enables faulthandler and asyncio debug mode, and sets sys.flags.dev_mode to True.
solid answer
~40 s`-X dev` (or `PYTHONDEVMODE=1`) is a **startup configuration** of a normal release build, not a different interpreter. It adds the `default` warning filter, so categories the default filters hide -- `ResourceWarning`, `DeprecationWarning`, `ImportWarning` -- are printed once per location; it installs debug hooks around CPython's memory allocations that detect buffer overruns and use-after-free from C code; it enables `faulthandler` so a hard crash dumps a Python traceback; it turns on asyncio debug mode; it makes `io.IOBase`'s finalizer log exceptions raised while closing; and it exposes itself as `sys.flags.dev_mode`. It changes no language semantics, so nothing that worked stops working -- you just see more. It is a switch for test runs, local development and reproductions, not a permanent production setting.
code
console · 4 lines$ python -X dev -c "import sys; print(sys.flags.dev_mode)"
True
$ PYTHONDEVMODE=1 python -c "import sys; print(sys.flags.dev_mode)"
Truego deeper
Be able to name the switch and two or three things it turns on -- hidden warnings become visible, crashes print a traceback, asyncio gets stricter -- and to say it needs no special build of Python.
Explain the mechanics: it is read at interpreter startup only, PYTHONDEVMODE=1 is the equivalent env var, sys.flags.dev_mode reports it, and tracemalloc is a separate switch you often pair with it.
Show where you put it in a real pipeline -- on for the whole test suite and staging, off or scoped in latency-sensitive production -- and be ready to say what it costs and how you measured that.
Own the policy: which classes of process run with it permanently, whether a dev-mode warning fails a build, and how you stop the output from becoming a wall of noise nobody reads.
### What development mode is Development mode is a **startup configuration of the CPython interpreter**, requested either with the command-line switch `-X dev` or with the environment variable `PYTHONDEVMODE=1`. It is not a different Python, not a recompile, and not a special build: the same release interpreter you already ship runs in a louder configuration that trades a little speed for extra runtime checking and diagnostics. Because the flag is read while the interpreter is starting up, **it cannot be switched on later from inside a running process**. You launch with it or you do not. What a running program *can* do is observe it: `sys.flags.dev_mode` is `True` in a development-mode interpreter and `False` otherwise, which is the hook a test harness or an expensive self-check should key off. ### What it actually switches on 1. **The `default` warning filter.** CPython's out-of-the-box filters silently discard several categories, notably `ResourceWarning`, `PendingDeprecationWarning`, `ImportWarning`, and `DeprecationWarning` outside `__main__`. Development mode adds a filter whose action is `default`, meaning every warning is printed once per unique source location. This is the part most people actually notice, because a codebase that has never been run this way usually produces a wall of output the first time. 2. **Debug hooks on the memory allocators.** Allocations made through CPython's allocator get extra bytes and known fill patterns around them, and every allocation and free is checked. That catches a C extension writing past the end of a buffer, freeing a block twice, or calling an allocation API from the wrong context -- failures that otherwise corrupt memory quietly and crash somewhere unrelated. 3. **`faulthandler`.** A fatal signal such as a segmentation fault dumps a Python-level traceback instead of dying silently, so you learn which Python frames were live when native code fell over. 4. **asyncio debug mode.** The event loop tracks where coroutines were created, complains about coroutines that were never awaited, and logs callbacks that block the loop for too long. 5. **`sys.flags.dev_mode`** is set to `True`. 6. **Small correctness checks.** `io.IOBase`'s finalizer logs exceptions raised while closing a file instead of swallowing them, and the `encoding`/`errors` arguments to text operations are validated even in cases where the interpreter would normally skip the check. ### What it deliberately does not do * **It does not enable `tracemalloc`.** Allocation tracking is a separate, considerably more expensive switch: `-X tracemalloc=5` or `PYTHONTRACEMALLOC=5`. Pairing the two is a common recipe, because it makes warnings about leaked resources report where the object was created. * **It is not a debug build.** A `--with-pydebug` CPython has a different ABI, extra interpreter assertions and much larger overhead; development mode runs on the ordinary release build you already deploy, and no extension module needs rebuilding. * **It does not enable `assert` statements.** Those are on by default already; only `-O` removes them. * **It does not change language semantics.** No program that worked correctly starts failing. Output that appears under development mode is describing a defect that was always there. ### How teams actually use it The usual placement is: on for the whole test suite, on in local development, on in staging, and off -- or deliberately scoped -- in a latency-sensitive production process. The environment-variable form matters more than it looks, because it is the only form that survives a launcher you do not control: a container entrypoint, a process supervisor, a task runner. Setting `PYTHONDEVMODE=1` in the environment also reaches child processes, since environment variables are inherited, whereas an interpreter flag applies only to the interpreter you typed it at. ### Reading it from inside the program `sys.flags.dev_mode` is the supported way for code to know it is running in this configuration, and it is genuinely useful. Expensive invariant checks -- validating a parsed structure field by field, verifying that a cache and its index agree, asserting that a buffer is the length the format promised -- can be guarded behind it, so they run in every test and local execution and cost nothing in the deployed process. That is a better pattern than a home-grown `DEBUG` flag, because it is set by the same switch that controls the interpreter's own extra checking, and there is exactly one thing to turn on. The cost is real but modest and workload-dependent: the allocator hooks make allocation-heavy code measurably slower and grow resident memory, asyncio debug mode adds bookkeeping per coroutine, and printing warnings from a hot path is not free. That is why the mode exists as a switch rather than a default, and why the interesting question is not *what does it do* but *for which process is the extra confidence worth the extra time*.
- How would you enable development mode for a process whose command line you do not control?Set `PYTHONDEVMODE=1` in the environment. It is read at interpreter startup exactly like `-X dev`, works from a container entrypoint or a supervisor unit where the command line is fixed, and -- because environment variables are inherited -- it also applies to interpreters the process spawns, which a bare `-X dev` on the parent's command line does not.
- Does `-X dev` give you the allocation traceback for a leaked object?No. Development mode installs debug hooks around allocations, but it does not record where each object was created. That is `tracemalloc`, requested separately with `-X tracemalloc=5` or `PYTHONTRACEMALLOC=5`. Running both together is the useful combination: dev mode surfaces the warning, `tracemalloc` attaches the traceback of the allocation site to it.
- Is running with `-X dev` the same as running a debug build of CPython?No. A `--with-pydebug` build is compiled differently: it carries interpreter-level assertions and reference-count checks, has a different ABI, forces extension modules to be rebuilt, and is far slower. Development mode is a runtime configuration of the ordinary release build -- you can enable it on the exact binary you deploy, and turn it off again by removing one flag.
It is the interpreter's equivalent of turning every dashboard warning light back on: the car drives the same, you simply stop being protected from knowing what is wrong.
saying these in an interview costs you the question
- Calls development mode a debug build of CPython
- Thinks it enables assert statements, which are already on unless -O
- Believes dev mode can be switched on mid-process at runtime
- Claims it changes semantics and can break working code
- Assumes it automatically enables tracemalloc allocation tracking
- Says it is free, so leave it on everywhere