diff options
author | Nick Coghlan <ncoghlan@gmail.com> | 2018-01-08 02:45:02 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-08 02:45:02 (GMT) |
commit | 9b99747386b690007027c3be2a5d7cfe3d3634f5 (patch) | |
tree | ba319d02ddc0e437bd0f90d520a4409efa7af6e2 | |
parent | d13889214a4c81b78fa8683d35bdbd17ff22f4fe (diff) | |
download | cpython-9b99747386b690007027c3be2a5d7cfe3d3634f5.zip cpython-9b99747386b690007027c3be2a5d7cfe3d3634f5.tar.gz cpython-9b99747386b690007027c3be2a5d7cfe3d3634f5.tar.bz2 |
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
-rw-r--r-- | Doc/library/exceptions.rst | 19 | ||||
-rw-r--r-- | Doc/library/warnings.rst | 221 | ||||
-rw-r--r-- | Doc/tools/susp-ignored.csv | 19 | ||||
-rw-r--r-- | Doc/using/cmdline.rst | 72 | ||||
-rw-r--r-- | Doc/whatsnew/3.7.rst | 40 | ||||
-rw-r--r-- | Lib/test/test_cmd_line.py | 2 | ||||
-rw-r--r-- | Lib/test/test_warnings/__init__.py | 36 | ||||
-rw-r--r-- | Lib/warnings.py | 4 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2018-01-05-20-54-27.bpo-31975.AmftlU.rst | 4 | ||||
-rw-r--r-- | Python/_warnings.c | 40 |
10 files changed, 333 insertions, 124 deletions
diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index c8d32cf..aa31410 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 f67f4bc..b04bd79 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 48dd53f..cfdd526 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 b1bd47f..1e9ed6e 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 9785d59..992d9ba 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 54ea377..a6b6634 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -558,6 +558,7 @@ class CmdLineTest(unittest.TestCase): expected_filters = "default::Warning" else: expected_filters = ("default::Warning " + "default::DeprecationWarning " "ignore::DeprecationWarning " "ignore::PendingDeprecationWarning " "ignore::ImportWarning " @@ -626,6 +627,7 @@ class CmdLineTest(unittest.TestCase): "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 039c96e..31ab94b 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -16,6 +16,8 @@ import warnings as original_warnings 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 @@ class FilterTests(BaseTest): 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 @@ class EnvironmentVariableTests(BaseTest): 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 @@ a=A() 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 f4331c8..76ad4da 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -519,8 +519,10 @@ except ImportError: # 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 0000000..98cfae0 --- /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 be8370da..c286364 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) { |