November 12, 2017·4 min read·engineeringpythonwarningsproposals

PEP 565 brings deprecation warnings back to scripts

PEP 565 proposes showing DeprecationWarning in __main__ while retaining quiet defaults for imported application code.

A warning about a deprecated API is useful only if it reaches someone who can change the call. Python has been hiding DeprecationWarning during ordinary execution, leaving test runners to expose it to developers. That policy keeps end users from seeing warnings about applications they do not maintain, but it also hides warnings from people writing small scripts without test suites. PEP 565 proposes a narrower default that distinguishes those situations through the module to which the warning is attributed.

Nick Coghlan's November 12 draft targets Python 3.7. It would display deprecation warnings attributed to __main__, while preserving the existing default for imported code. The commit introducing the proposal establishes the version of the design discussed here. This is proposed behavior, so running a current Python interpreter does not yet gain this filter merely because the PEP exists.

I regard advance notice of dependency changes as necessary information for maintaining Python programs. The ecosystem is useful precisely because application code delegates substantial work to libraries. A warning system should give the author of that code an opportunity to respond without making every user of a Python application diagnose its internal dependencies.

The quiet default had a specific purpose

The draft traces the existing policy to Python 2.7 and 3.2. It explains that development tools implemented in Python can themselves use deprecated APIs. Displaying those warnings on every invocation exposes the tool's implementation details to someone trying to use its output. The user's most useful response may simply be reporting the warning to the tool's maintainers.

Hiding the warnings addressed that audience problem. The standard unittest runner overrides the filter during test execution, giving developers a place to see the information. However, the PEP identifies several groups missed by that arrangement, including authors of scripts without Python-level tests and users of other test runners that do not make the same adjustment.

The proposed change therefore uses an approximation of authorship. Code entered at the interactive prompt or executed directly in a single-file script is attributed to __main__. A person running it is more likely to be able to edit it than a person launching a packaged tool's installed entry point. The module name does not prove who owns the code, but it gives the filter a concrete condition to evaluate.

Warning attribution determines visibility

The draft adds the following rule before the general suppression of deprecation warnings.

default::DeprecationWarning:__main__
ignore::DeprecationWarning

The first rule reports the first occurrence of the applicable warning under the default action. Other deprecation warnings remain hidden under the second rule. PEP 565 retains the remaining existing filters, including those for pending deprecations and resource warnings. This is one targeted change to a default filter list that already distinguishes several warning categories.

The warning's nominal location matters. The draft explicitly connects that location to the stacklevel argument of warnings.warn(). A library can attribute its warning to the calling code, so the important module is the one assigned to the warning. Merely asking which file contains the call to warnings.warn() would not describe the proposed behavior correctly.

Imported implementation code remains quiet by default. The draft names code imported by a zip application's __main__.py, an executable package, or an installed script wrapper as examples. Having an entry point called __main__ does not make every module reached from that entry point eligible for visible deprecations. The location attributed to the particular warning remains the deciding condition.

Different warnings address different people

The draft also proposes documentation guidance for FutureWarning when a compatibility warning should reach application users. A deprecated configuration-file setting is its example. The user can change that setting, so hiding the notice merely because it originates in packaged code would defeat its purpose.

DeprecationWarning continues to address developers whose API usage needs to change. PendingDeprecationWarning remains information primarily exposed by test configurations that choose to report it. The distinctions concern the intended recipient, rather than a universal ranking of how serious the underlying problem is.

I prefer that audience-based account because it explains how a warning can be accurate and still appear in the wrong place. Printing more warnings does not by itself improve maintenance. The person reading the output needs a plausible action associated with the information being shown.

The proposal leaves known gaps. It says unittest currently changes filters during execution rather than discovery, so import-time warnings can remain hidden. Its in-process filter adjustment also does not configure subprocesses. Moving code from a single script into support modules can hide its warnings even when it still lacks tests. The draft gives explicit warning-filter options for such cases and excludes broader solutions from this PEP. Its immediate proposal improves visibility for directly executed code while leaving those separate execution and attribution problems for further work.