[Python-checkins] bpo-31975 (PEP 565): Show DeprecationWarning in __main__ (GH-4458)

Nick Coghlan webhook-mailer at python.org
Sun Jan 7 21:45:05 EST 2018


https://github.com/python/cpython/commit/9b99747386b690007027c3be2a5d7cfe3d3634f5
commit: 9b99747386b690007027c3be2a5d7cfe3d3634f5
branch: master
author: Nick Coghlan <ncoghlan at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-01-08T12:45:02+10:00
summary:

bpo-31975 (PEP 565): Show DeprecationWarning in __main__ (GH-4458)

- primary change is to add a new default filter entry for
  'default::DeprecationWarning:__main__'
- secondary change is an internal one to cope with plain
  strings in the warning module's internal filter list
  (this avoids the need to create a compiled regex object
  early on during interpreter startup)
- assorted documentation updates, including many more
  examples of configuring the warnings settings
- additional tests to ensure that both the pure Python and
  the C accelerated warnings modules have the expected
  default configuration

files:
A Misc/NEWS.d/next/Core and Builtins/2018-01-05-20-54-27.bpo-31975.AmftlU.rst
M Doc/library/exceptions.rst
M Doc/library/warnings.rst
M Doc/tools/susp-ignored.csv
M Doc/using/cmdline.rst
M Doc/whatsnew/3.7.rst
M Lib/test/test_cmd_line.py
M Lib/test/test_warnings/__init__.py
M Lib/warnings.py
M Python/_warnings.c

diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst
index c8d32cff4e0..aa3141053b3 100644
--- a/Doc/library/exceptions.rst
+++ b/Doc/library/exceptions.rst
@@ -661,11 +661,13 @@ depending on the system error code.
    :pep:`3151` - Reworking the OS and IO exception hierarchy
 
 
+.. _warning-categories-as-exceptions:
+
 Warnings
 --------
 
-The following exceptions are used as warning categories; see the :mod:`warnings`
-module for more information.
+The following exceptions are used as warning categories; see the
+:ref:`warning-categories` documentation for more details.
 
 .. exception:: Warning
 
@@ -679,12 +681,14 @@ module for more information.
 
 .. exception:: DeprecationWarning
 
-   Base class for warnings about deprecated features.
+   Base class for warnings about deprecated features when those warnings are
+   intended for other Python developers.
 
 
 .. exception:: PendingDeprecationWarning
 
-   Base class for warnings about features which will be deprecated in the future.
+   Base class for warnings about features which will be deprecated in the
+   future.
 
 
 .. exception:: SyntaxWarning
@@ -699,8 +703,8 @@ module for more information.
 
 .. exception:: FutureWarning
 
-   Base class for warnings about constructs that will change semantically in the
-   future.
+   Base class for warnings about deprecated features when those warnings are
+   intended for end users of applications that are written in Python.
 
 
 .. exception:: ImportWarning
@@ -720,7 +724,8 @@ module for more information.
 
 .. exception:: ResourceWarning
 
-   Base class for warnings related to resource usage.
+   Base class for warnings related to resource usage. Ignored by the default
+   warning filters.
 
    .. versionadded:: 3.2
 
diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst
index f67f4bc24c4..b04bd79e4bb 100644
--- a/Doc/library/warnings.rst
+++ b/Doc/library/warnings.rst
@@ -51,8 +51,17 @@ Warning Categories
 ------------------
 
 There are a number of built-in exceptions that represent warning categories.
-This categorization is useful to be able to filter out groups of warnings.  The
-following warnings category classes are currently defined:
+This categorization is useful to be able to filter out groups of warnings.
+
+While these are technically
+:ref:`built-in exceptions <warning-categories-as-exceptions>`, they are
+documented here, because conceptually they belong to the warnings mechanism.
+
+User code can define additional warning categories by subclassing one of the
+standard warning categories.  A warning category must always be a subclass of
+the :exc:`Warning` class.
+
+The following warnings category classes are currently defined:
 
 .. tabularcolumns:: |l|p{0.6\linewidth}|
 
@@ -66,7 +75,9 @@ following warnings category classes are currently defined:
 | :exc:`UserWarning`               | The default category for :func:`warn`.        |
 +----------------------------------+-----------------------------------------------+
 | :exc:`DeprecationWarning`        | Base category for warnings about deprecated   |
-|                                  | features (ignored by default).                |
+|                                  | features when those warnings are intended for |
+|                                  | other Python developers (ignored by default,  |
+|                                  | unless triggered by code in ``__main__``).    |
 +----------------------------------+-----------------------------------------------+
 | :exc:`SyntaxWarning`             | Base category for warnings about dubious      |
 |                                  | syntactic features.                           |
@@ -74,8 +85,10 @@ following warnings category classes are currently defined:
 | :exc:`RuntimeWarning`            | Base category for warnings about dubious      |
 |                                  | runtime features.                             |
 +----------------------------------+-----------------------------------------------+
-| :exc:`FutureWarning`             | Base category for warnings about constructs   |
-|                                  | that will change semantically in the future.  |
+| :exc:`FutureWarning`             | Base category for warnings about deprecated   |
+|                                  | features when those warnings are intended for |
+|                                  | end users of applications that are written in |
+|                                  | Python.                                       |
 +----------------------------------+-----------------------------------------------+
 | :exc:`PendingDeprecationWarning` | Base category for warnings about features     |
 |                                  | that will be deprecated in the future         |
@@ -95,13 +108,12 @@ following warnings category classes are currently defined:
 |                                  | resource usage.                               |
 +----------------------------------+-----------------------------------------------+
 
-
-While these are technically built-in exceptions, they are documented here,
-because conceptually they belong to the warnings mechanism.
-
-User code can define additional warning categories by subclassing one of the
-standard warning categories.  A warning category must always be a subclass of
-the :exc:`Warning` class.
+.. versionchanged:: 3.7
+  Previously :exc:`DeprecationWarning` and :exc:`FutureWarning` were
+  distinguished based on whether a feature was being removed entirely or
+  changing its behaviour. They are now distinguished based on their
+  intended audience and the way they're handled by the default warnings
+  filters.
 
 
 .. _warning-filter:
@@ -114,7 +126,7 @@ into errors (raising an exception).
 
 Conceptually, the warnings filter maintains an ordered list of filter
 specifications; any specific warning is matched against each filter
-specification in the list in turn until a match is found; the match determines
+specification in the list in turn until a match is found; the filter determines
 the disposition of the match.  Each entry is a tuple of the form (*action*,
 *message*, *category*, *module*, *lineno*), where:
 
@@ -123,19 +135,19 @@ the disposition of the match.  Each entry is a tuple of the form (*action*,
   +---------------+----------------------------------------------+
   | Value         | Disposition                                  |
   +===============+==============================================+
+  | ``"default"`` | print the first occurrence of matching       |
+  |               | warnings for each location (module +         |
+  |               | line number) where the warning is issued     |
+  +---------------+----------------------------------------------+
   | ``"error"``   | turn matching warnings into exceptions       |
   +---------------+----------------------------------------------+
   | ``"ignore"``  | never print matching warnings                |
   +---------------+----------------------------------------------+
   | ``"always"``  | always print matching warnings               |
   +---------------+----------------------------------------------+
-  | ``"default"`` | print the first occurrence of matching       |
-  |               | warnings for each location where the warning |
-  |               | is issued                                    |
-  +---------------+----------------------------------------------+
   | ``"module"``  | print the first occurrence of matching       |
   |               | warnings for each module where the warning   |
-  |               | is issued                                    |
+  |               | is issued (regardless of line number)        |
   +---------------+----------------------------------------------+
   | ``"once"``    | print only the first occurrence of matching  |
   |               | warnings, regardless of location             |
@@ -157,33 +169,119 @@ the disposition of the match.  Each entry is a tuple of the form (*action*,
 Since the :exc:`Warning` class is derived from the built-in :exc:`Exception`
 class, to turn a warning into an error we simply raise ``category(message)``.
 
+If a warning is reported and doesn't match any registered filter then the
+"default" action is applied (hence its name).
+
+
+.. _describing-warning-filters:
+
+Describing Warning Filters
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 The warnings filter is initialized by :option:`-W` options passed to the Python
-interpreter command line.  The interpreter saves the arguments for all
-:option:`-W` options without interpretation in ``sys.warnoptions``; the
-:mod:`warnings` module parses these when it is first imported (invalid options
-are ignored, after printing a message to ``sys.stderr``).
+interpreter command line and the :envvar:`PYTHONWARNINGS` environment variable.
+The interpreter saves the arguments for all supplied entries without
+interpretation in ``sys.warnoptions``; the :mod:`warnings` module parses these
+when it is first imported (invalid options are ignored, after printing a
+message to ``sys.stderr``).
+
+Individual warnings filters are specified as a sequence of fields separated by
+colons::
 
+   action:message:category:module:line
 
-Default Warning Filters
-~~~~~~~~~~~~~~~~~~~~~~~
+The meaning of each of these fields is as described in :ref:`warning-filter`.
+When listing multiple filters on a single line (as for
+:envvar:`PYTHONWARNINGS`), the individual filters are separated by commas,and
+the filters listed later take precedence over those listed before them (as
+they're applied left-to-right, and the most recently applied filters take
+precedence over earlier ones).
+
+Commonly used warning filters apply to either all warnings, warnings in a
+particular category, or warnings raised by particular modules or packages.
+Some examples::
+
+   default                      # Show all warnings (even those ignored by default)
+   ignore                       # Ignore all warnings
+   error                        # Convert all warnings to errors
+   error::ResourceWarning       # Treat ResourceWarning messages as errors
+   default::DeprecationWarning  # Show DeprecationWarning messages
+   ignore,default:::mymodule    # Only report warnings triggered by "mymodule"
+   error:::mymodule[.*]         # Convert warnings to errors in "mymodule"
+                                # and any subpackages of "mymodule"
+
+
+.. _default-warning-filter:
+
+Default Warning Filter
+~~~~~~~~~~~~~~~~~~~~~~
 
 By default, Python installs several warning filters, which can be overridden by
-the command-line options passed to :option:`-W` and calls to
-:func:`filterwarnings`.
+the :option:`-W` command-line option, the :envvar:`PYTHONWARNINGS` environment
+variable and calls to :func:`filterwarnings`.
 
-* :exc:`DeprecationWarning` and :exc:`PendingDeprecationWarning`, and
-  :exc:`ImportWarning` are ignored.
+In regular release builds, the default warning filter has the following entries
+(in order of precedence)::
 
-* :exc:`BytesWarning` is ignored unless the :option:`-b` option is given once or
-  twice; in this case this warning is either printed (``-b``) or turned into an
-  exception (``-bb``).
+    default::DeprecationWarning:__main__
+    ignore::DeprecationWarning
+    ignore::PendingDeprecationWarning
+    ignore::ImportWarning
+    ignore::ResourceWarning
 
-* :exc:`ResourceWarning` is ignored unless Python was built in debug mode.
+In debug builds, the list of default warning filters is empty.
 
 .. versionchanged:: 3.2
    :exc:`DeprecationWarning` is now ignored by default in addition to
    :exc:`PendingDeprecationWarning`.
 
+.. versionchanged:: 3.7
+  :exc:`DeprecationWarning` is once again shown by default when triggered
+  directly by code in ``__main__``.
+
+.. versionchanged:: 3.7
+  :exc:`BytesWarning` no longer appears in the default filter list and is
+  instead configured via :data:`sys.warnoptions` when :option:`-b` is specified
+  twice.
+
+
+.. _warning-disable:
+
+Overriding the default filter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Developers of applications written in Python may wish to hide *all* Python level
+warnings from their users by default, and only display them when running tests
+or otherwise working on the application. The :data:`sys.warnoptions` attribute
+used to pass filter configurations to the interpreter can be used as a marker to
+indicate whether or not warnings should be disabled::
+
+    import sys
+
+    if not sys.warnoptions:
+        import warnings
+        warnings.simplefilter("ignore")
+
+Developers of test runners for Python code are advised to instead ensure that
+*all* warnings are displayed by default for the code under test, using code
+like::
+
+    import sys
+
+    if not sys.warnoptions:
+        import os, warnings
+        warnings.simplefilter("default") # Change the filter in this process
+        os.environ["PYTHONWARNINGS"] = "default" # Also affect subprocesses
+
+Finally, developers of interactive shells that run user code in a namespace
+other than ``__main__`` are advised to ensure that :exc:`DeprecationWarning`
+messages are made visible by default, using code like the following (where
+``user_ns`` is the module used to execute code entered interactively)::
+
+    import warnings
+    warnings.filterwarnings("default", category=DeprecationWarning,
+                                       module=user_ns.get("__name__"))
+
 
 .. _warning-suppress:
 
@@ -191,7 +289,8 @@ Temporarily Suppressing Warnings
 --------------------------------
 
 If you are using code that you know will raise a warning, such as a deprecated
-function, but do not want to see the warning, then it is possible to suppress
+function, but do not want to see the warning (even when warnings have been
+explicitly configured via the command line), then it is possible to suppress
 the warning using the :class:`catch_warnings` context manager::
 
     import warnings
@@ -261,38 +360,30 @@ entries from the warnings list before each new operation).
 
 .. _warning-ignored:
 
-Updating Code For New Versions of Python
-----------------------------------------
-
-Warnings that are only of interest to the developer are ignored by default. As
-such you should make sure to test your code with typically ignored warnings
-made visible. You can do this from the command-line by passing :option:`-Wd <-W>`
-to the interpreter (this is shorthand for :option:`!-W default`).  This enables
-default handling for all warnings, including those that are ignored by default.
-To change what action is taken for encountered warnings you simply change what
-argument is passed to :option:`-W`, e.g. :option:`!-W error`. See the
-:option:`-W` flag for more details on what is possible.
-
-To programmatically do the same as :option:`!-Wd`, use::
-
-  warnings.simplefilter('default')
-
-Make sure to execute this code as soon as possible. This prevents the
-registering of what warnings have been raised from unexpectedly influencing how
-future warnings are treated.
-
-Having certain warnings ignored by default is done to prevent a user from
-seeing warnings that are only of interest to the developer. As you do not
-necessarily have control over what interpreter a user uses to run their code,
-it is possible that a new version of Python will be released between your
-release cycles.  The new interpreter release could trigger new warnings in your
-code that were not there in an older interpreter, e.g.
-:exc:`DeprecationWarning` for a module that you are using. While you as a
-developer want to be notified that your code is using a deprecated module, to a
-user this information is essentially noise and provides no benefit to them.
-
-The :mod:`unittest` module has been also updated to use the ``'default'``
-filter while running tests.
+Updating Code For New Versions of Dependencies
+----------------------------------------------
+
+Warning categories that are primarily of interest to Python developers (rather
+than end users of applications written in Python) are ignored by default.
+
+Notably, this "ignored by default" list includes :exc:`DeprecationWarning`
+(for every module except ``__main__``), which means developers should make sure
+to test their code with typically ignored warnings made visible in order to
+receive timely notifications of future breaking API changes (whether in the
+standard library or third party packages).
+
+In the ideal case, the code will have a suitable test suite, and the test runner
+will take care of implicitly enabling all warnings when running tests
+(the test runner provided by the :mod:`unittest` module does this).
+
+In less ideal cases, applications can be checked for use of deprecated
+interfaces by passing :option:`-Wd <-W>` to the Python interpreter (this is
+shorthand for :option:`!-W default`) or setting ``PYTHONWARNINGS=default`` in
+the environment. This enables default handling for all warnings, including those
+that are ignored by default. To change what action is taken for encountered
+warnings you can change what argument is passed to :option:`-W` (e.g.
+:option:`!-W error`). See the :option:`-W` flag for more details on what is
+possible.
 
 
 .. _warning-functions:
diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv
index 48dd53f85d2..cfdd5266c51 100644
--- a/Doc/tools/susp-ignored.csv
+++ b/Doc/tools/susp-ignored.csv
@@ -259,12 +259,8 @@ tutorial/stdlib2,,:start,extra = data[start:start+extra_size]
 tutorial/stdlib2,,:start,"fields = struct.unpack('<IIIHH', data[start:start+16])"
 tutorial/stdlib2,,:start,filename = data[start:start+filenamesize]
 tutorial/stdlib2,,:Warning,WARNING:root:Warning:config file server.conf not found
-using/cmdline,,:category,action:message:category:module:line
 using/cmdline,,:errorhandler,:errorhandler
-using/cmdline,,:line,action:message:category:module:line
 using/cmdline,,:line,file:line: category: message
-using/cmdline,,:message,action:message:category:module:line
-using/cmdline,,:module,action:message:category:module:line
 using/unix,,:Packaging,https://en.opensuse.org/Portal:Packaging
 whatsnew/2.0,,:len,
 whatsnew/2.3,,::,
@@ -302,6 +298,20 @@ whatsnew/3.2,,:prefix,zope-conf = ${custom:prefix}/etc/zope.conf
 library/re,,`,!#$%&'*+-.^_`|~:
 library/re,,`,!\#\$%\&'\*\+\-\.\^_`\|\~:
 library/tarfile,,:xz,'x:xz'
+library/warnings,,:message,action:message:category:module:line
+library/warnings,,:category,action:message:category:module:line
+library/warnings,,:module,action:message:category:module:line
+library/warnings,,:line,action:message:category:module:line
+library/warnings,,::,error::ResourceWarning
+library/warnings,,::,default::DeprecationWarning
+library/warnings,,::,default:::mymodule
+library/warnings,,:mymodule,default:::mymodule
+library/warnings,,::,error:::mymodule
+library/warnings,,:mymodule,error:::mymodule
+library/warnings,,::,ignore::DeprecationWarning
+library/warnings,,::,ignore::PendingDeprecationWarning
+library/warnings,,::,ignore::ImportWarning
+library/warnings,,::,ignore::ResourceWarning
 library/xml.etree.elementtree,,:sometag,prefix:sometag
 library/xml.etree.elementtree,,:fictional,"<actors xmlns:fictional=""http://characters.example.com"""
 library/xml.etree.elementtree,,:character,<fictional:character>Lancelot</fictional:character>
@@ -330,3 +340,4 @@ whatsnew/3.7,,`,'`'
 whatsnew/3.7,,::,error::BytesWarning
 whatsnew/changelog,,::,error::BytesWarning
 whatsnew/changelog,,::,default::BytesWarning
+whatsnew/changelog,,::,default::DeprecationWarning
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index b1bd47fa6b9..1e9ed6e645a 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -356,49 +356,27 @@ Miscellaneous options
    :option:`-W` options are ignored (though, a warning message is printed about
    invalid options when the first warning is issued).
 
-   Warnings can also be controlled from within a Python program using the
+   Warnings can also be controlled using the :envvar:`PYTHONWARNINGS`
+   environment variable and from within a Python program using the
    :mod:`warnings` module.
 
-   The simplest form of argument is one of the following action strings (or a
-   unique abbreviation):
-
-   ``ignore``
-      Ignore all warnings.
-   ``default``
-      Explicitly request the default behavior (printing each warning once per
-      source line).
-   ``all``
-      Print a warning each time it occurs (this may generate many messages if a
-      warning is triggered repeatedly for the same source line, such as inside a
-      loop).
-   ``module``
-      Print each warning only the first time it occurs in each module.
-   ``once``
-      Print each warning only the first time it occurs in the program.
-   ``error``
-      Raise an exception instead of printing a warning message.
-
-   The full form of argument is::
-
-       action:message:category:module:line
-
-   Here, *action* is as explained above but only applies to messages that match
-   the remaining fields.  Empty fields match all values; trailing empty fields
-   may be omitted.  The *message* field matches the start of the warning message
-   printed; this match is case-insensitive.  The *category* field matches the
-   warning category.  This must be a class name; the match tests whether the
-   actual warning category of the message is a subclass of the specified warning
-   category.  The full class name must be given.  The *module* field matches the
-   (fully-qualified) module name; this match is case-sensitive.  The *line*
-   field matches the line number, where zero matches all line numbers and is
-   thus equivalent to an omitted line number.
+   The simplest settings apply a particular action unconditionally to all
+   warnings emitted by a process (even those that are otherwise ignored by
+   default)::
 
-   .. seealso::
-      :mod:`warnings` -- the warnings module
+       -Wdefault  # Warn once per call location
+       -Werror    # Convert to exceptions
+       -Walways   # Warn every time
+       -Wmodule   # Warn once per calling module
+       -Wonce     # Warn once per Python process
+       -Wignore   # Never warn
 
-      :pep:`230` -- Warning framework
+   The action names can be abbreviated as desired (e.g. ``-Wi``, ``-Wd``,
+   ``-Wa``, ``-We``) and the interpreter will resolve them to the appropriate
+   action name.
 
-      :envvar:`PYTHONWARNINGS`
+   See :ref:`warning-filter` and :ref:`describing-warning-filters` for more
+   details.
 
 
 .. cmdoption:: -x
@@ -659,7 +637,23 @@ conflict.
 
    This is equivalent to the :option:`-W` option. If set to a comma
    separated string, it is equivalent to specifying :option:`-W` multiple
-   times.
+   times, with filters later in the list taking precedence over those earlier
+   in the list.
+
+   The simplest settings apply a particular action unconditionally to all
+   warnings emitted by a process (even those that are otherwise ignored by
+   default)::
+
+       PYTHONWARNINGS=default  # Warn once per call location
+       PYTHONWARNINGS=error    # Convert to exceptions
+       PYTHONWARNINGS=always   # Warn every time
+       PYTHONWARNINGS=module   # Warn once per calling module
+       PYTHONWARNINGS=once     # Warn once per Python process
+       PYTHONWARNINGS=ignore   # Never warn
+
+   See :ref:`warning-filter` and :ref:`describing-warning-filters` for more
+   details.
+
 
 .. envvar:: PYTHONFAULTHANDLER
 
diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst
index 9785d599dd3..992d9ba6e58 100644
--- a/Doc/whatsnew/3.7.rst
+++ b/Doc/whatsnew/3.7.rst
@@ -70,6 +70,7 @@ Summary -- Release highlights
 New Features
 ============
 
+
 .. _whatsnew37-pep538:
 
 PEP 538: Legacy C Locale Coercion
@@ -107,6 +108,7 @@ locale remains active when the core interpreter is initialized.
     :pep:`538` -- Coercing the legacy C locale to a UTF-8 based locale
        PEP written and implemented by Nick Coghlan.
 
+
 .. _whatsnew37-pep553:
 
 PEP 553: Built-in breakpoint()
@@ -203,6 +205,44 @@ resolution on Linux and Windows.
        PEP written and implemented by Victor Stinner
 
 
+.. _whatsnew37-pep565:
+
+PEP 565: Show DeprecationWarning in ``__main__``
+------------------------------------------------
+
+The default handling of :exc:`DeprecationWarning` has been changed such that
+these warnings are once more shown by default, but only when the code
+triggering them is running directly in the ``__main__`` module. As a result,
+developers of single file scripts and those using Python interactively should
+once again start seeing deprecation warnings for the APIs they use, but
+deprecation warnings triggered by imported application, library and framework
+modules will continue to be hidden by default.
+
+As a result of this change, the standard library now allows developers to choose
+between three different deprecation warning behaviours:
+
+* :exc:`FutureWarning`: always displayed by default, recommended for warnings
+  intended to be seen by application end users (e.g. for deprecated application
+  configuration settings).
+* :exc:`DeprecationWarning`: displayed by default only in ``__main__`` and when
+  running tests, recommended for warnings intended to be seen by other Python
+  developers where a version upgrade may result in changed behaviour or an
+  error.
+* :exc:`PendingDeprecationWarning`: displayed by default only when running
+  tests, intended for cases where a future version upgrade will change the
+  warning category to :exc:`DeprecationWarning` or :exc:`FutureWarning`.
+
+Previously both :exc:`DeprecationWarning` and :exc:`PendingDeprecationWarning`
+were only visible when running tests, which meant that developers primarily
+writing single file scripts or using Python interactively could be surprised
+by breaking changes in the APIs they used.
+
+.. seealso::
+
+    :pep:`565` -- Show DeprecationWarning in ``__main__``
+      PEP written and implemented by Nick Coghlan
+
+
 PEP 540: Add a new UTF-8 mode
 -----------------------------
 
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index 54ea3773a06..a6b663403f4 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -558,6 +558,7 @@ def test_xdev(self):
             expected_filters = "default::Warning"
         else:
             expected_filters = ("default::Warning "
+                                "default::DeprecationWarning "
                                 "ignore::DeprecationWarning "
                                 "ignore::PendingDeprecationWarning "
                                 "ignore::ImportWarning "
@@ -626,6 +627,7 @@ def test_warnings_filter_precedence(self):
                             "always::UserWarning")
         if not Py_DEBUG:
             expected_filters += (" "
+                                 "default::DeprecationWarning "
                                  "ignore::DeprecationWarning "
                                  "ignore::PendingDeprecationWarning "
                                  "ignore::ImportWarning "
diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index 039c96e02c9..31ab94b7471 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -16,6 +16,8 @@
 py_warnings = support.import_fresh_module('warnings', blocked=['_warnings'])
 c_warnings = support.import_fresh_module('warnings', fresh=['_warnings'])
 
+Py_DEBUG = hasattr(sys, 'gettotalrefcount')
+
 @contextmanager
 def warnings_state(module):
     """Use a specific warnings implementation in warning_tests."""
@@ -320,6 +322,7 @@ def test_simplefilter_duplicate_filters(self):
                 self.module.filters[0][0], "error",
                 "simplefilter did not promote filter to the beginning of list"
             )
+
     def test_append_duplicate(self):
         with original_warnings.catch_warnings(module=self.module,
                 record=True) as w:
@@ -1143,6 +1146,37 @@ def test_conflicting_envvar_and_command_line(self):
              b"  File \"<string>\", line 1, in <module>",
              b"DeprecationWarning: Message"])
 
+    def test_default_filter_configuration(self):
+        pure_python_api = self.module is py_warnings
+        if Py_DEBUG:
+            expected_default_filters = []
+        else:
+            if pure_python_api:
+                main_module_filter = re.compile("__main__")
+            else:
+                main_module_filter = "__main__"
+            expected_default_filters = [
+                ('default', None, DeprecationWarning, main_module_filter, 0),
+                ('ignore', None, DeprecationWarning, None, 0),
+                ('ignore', None, PendingDeprecationWarning, None, 0),
+                ('ignore', None, ImportWarning, None, 0),
+                ('ignore', None, ResourceWarning, None, 0),
+            ]
+        expected_output = [str(f).encode() for f in expected_default_filters]
+
+        if pure_python_api:
+            # Disable the warnings acceleration module in the subprocess
+            code = "import sys; sys.modules.pop('warnings', None); sys.modules['_warnings'] = None; "
+        else:
+            code = ""
+        code += "import warnings; [print(f) for f in warnings.filters]"
+
+        rc, stdout, stderr = assert_python_ok("-c", code, __isolated=True)
+        stdout_lines = [line.strip() for line in stdout.splitlines()]
+        self.maxDiff = None
+        self.assertEqual(stdout_lines, expected_output)
+
+
     @unittest.skipUnless(sys.getfilesystemencoding() != 'ascii',
                          'requires non-ascii filesystemencoding')
     def test_nonascii(self):
@@ -1192,7 +1226,7 @@ def __del__(self):
         rc, out, err = assert_python_ok("-c", code)
         # note: "__main__" filename is not correct, it should be the name
         # of the script
-        self.assertEqual(err, b'__main__:7: UserWarning: test')
+        self.assertEqual(err.decode(), '__main__:7: UserWarning: test')
 
     def test_late_resource_warning(self):
         # Issue #21925: Emitting a ResourceWarning late during the Python
diff --git a/Lib/warnings.py b/Lib/warnings.py
index f4331c8ac38..76ad4dac018 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -519,8 +519,10 @@ def _filters_mutated():
 # Module initialization
 _processoptions(sys.warnoptions)
 if not _warnings_defaults:
-    # Several warning categories are ignored by default in Py_DEBUG builds
+    # Several warning categories are ignored by default in regular builds
     if not hasattr(sys, 'gettotalrefcount'):
+        filterwarnings("default", category=DeprecationWarning,
+                       module="__main__", append=1)
         simplefilter("ignore", category=DeprecationWarning, append=1)
         simplefilter("ignore", category=PendingDeprecationWarning, append=1)
         simplefilter("ignore", category=ImportWarning, append=1)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-01-05-20-54-27.bpo-31975.AmftlU.rst b/Misc/NEWS.d/next/Core and Builtins/2018-01-05-20-54-27.bpo-31975.AmftlU.rst
new file mode 100644
index 00000000000..98cfae0dd8c
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-01-05-20-54-27.bpo-31975.AmftlU.rst	
@@ -0,0 +1,4 @@
+The default warning filter list now starts with a
+"default::DeprecationWarning:__main__" entry, so deprecation warnings are
+once again shown by default in single-file scripts and at the interactive
+prompt.
diff --git a/Python/_warnings.c b/Python/_warnings.c
index be8370da06d..c286364dda0 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -12,6 +12,7 @@ MODULE_NAME " provides basic warning filtering support.\n"
 _Py_IDENTIFIER(argv);
 _Py_IDENTIFIER(stderr);
 #ifndef Py_DEBUG
+_Py_IDENTIFIER(default);
 _Py_IDENTIFIER(ignore);
 #endif
 
@@ -22,8 +23,20 @@ check_matched(PyObject *obj, PyObject *arg)
     _Py_IDENTIFIER(match);
     int rc;
 
+    /* A 'None' filter always matches */
     if (obj == Py_None)
         return 1;
+
+    /* An internal plain text default filter must match exactly */
+    if (PyUnicode_CheckExact(obj)) {
+        int cmp_result = PyUnicode_Compare(obj, arg);
+        if (cmp_result == -1 && PyErr_Occurred()) {
+            return -1;
+        }
+        return !cmp_result;
+    }
+
+    /* Otherwise assume a regex filter and call its match() method */
     result = _PyObject_CallMethodIdObjArgs(obj, &PyId_match, arg, NULL);
     if (result == NULL)
         return -1;
@@ -1158,16 +1171,27 @@ static PyMethodDef warnings_functions[] = {
 
 #ifndef Py_DEBUG
 static PyObject *
-create_filter(PyObject *category, _Py_Identifier *id)
+create_filter(PyObject *category, _Py_Identifier *id, const char *modname)
 {
+    PyObject *modname_obj = NULL;
     PyObject *action_str = _PyUnicode_FromId(id);
     if (action_str == NULL) {
         return NULL;
     }
 
+    /* Default to "no module name" for initial filter set */
+    if (modname != NULL) {
+        modname_obj = PyUnicode_InternFromString(modname);
+        if (modname_obj == NULL) {
+            return NULL;
+        }
+    } else {
+        modname_obj = Py_None;
+    }
+
     /* This assumes the line number is zero for now. */
     return PyTuple_Pack(5, action_str, Py_None,
-                        category, Py_None, _PyLong_Zero);
+                        category, modname_obj, _PyLong_Zero);
 }
 #endif
 
@@ -1180,20 +1204,22 @@ init_filters(void)
     return PyList_New(0);
 #else
     /* Other builds ignore a number of warning categories by default */
-    PyObject *filters = PyList_New(4);
+    PyObject *filters = PyList_New(5);
     if (filters == NULL) {
         return NULL;
     }
 
     size_t pos = 0;  /* Post-incremented in each use. */
     PyList_SET_ITEM(filters, pos++,
-                    create_filter(PyExc_DeprecationWarning, &PyId_ignore));
+                    create_filter(PyExc_DeprecationWarning, &PyId_default, "__main__"));
+    PyList_SET_ITEM(filters, pos++,
+                    create_filter(PyExc_DeprecationWarning, &PyId_ignore, NULL));
     PyList_SET_ITEM(filters, pos++,
-                    create_filter(PyExc_PendingDeprecationWarning, &PyId_ignore));
+                    create_filter(PyExc_PendingDeprecationWarning, &PyId_ignore, NULL));
     PyList_SET_ITEM(filters, pos++,
-                    create_filter(PyExc_ImportWarning, &PyId_ignore));
+                    create_filter(PyExc_ImportWarning, &PyId_ignore, NULL));
     PyList_SET_ITEM(filters, pos++,
-                    create_filter(PyExc_ResourceWarning, &PyId_ignore));
+                    create_filter(PyExc_ResourceWarning, &PyId_ignore, NULL));
 
     for (size_t x = 0; x < pos; x++) {
         if (PyList_GET_ITEM(filters, x) == NULL) {



More information about the Python-checkins mailing list