diff options
author | Inada Naoki <songofacandy@gmail.com> | 2022-04-30 06:53:29 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-30 06:53:29 (GMT) |
commit | 354ace8b07e7d445fd2de713b6af1271536adce0 (patch) | |
tree | 6827337429f78065731f6136975b4bea47dd8e0d | |
parent | c7b7f12b8609f932a23a9bc96a5de7cd9ecd5723 (diff) | |
download | cpython-354ace8b07e7d445fd2de713b6af1271536adce0.zip cpython-354ace8b07e7d445fd2de713b6af1271536adce0.tar.gz cpython-354ace8b07e7d445fd2de713b6af1271536adce0.tar.bz2 |
gh-91954: Emit EncodingWarning from locale and subprocess (GH-91977)
locale.getpreferredencoding() and subprocess.Popen() emit EncodingWarning
-rw-r--r-- | Doc/library/subprocess.rst | 14 | ||||
-rw-r--r-- | Lib/locale.py | 11 | ||||
-rw-r--r-- | Lib/subprocess.py | 38 | ||||
-rw-r--r-- | Lib/test/test_subprocess.py | 14 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-04-27-13-30-26.gh-issue-91954.cC7ga_.rst | 2 |
5 files changed, 65 insertions, 14 deletions
diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index fca5549..6a334ac 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -619,7 +619,7 @@ functions. If *encoding* or *errors* are specified, or *text* is true, the file objects *stdin*, *stdout* and *stderr* are opened in text mode with the specified - encoding and *errors*, as described above in :ref:`frequently-used-arguments`. + *encoding* and *errors*, as described above in :ref:`frequently-used-arguments`. The *universal_newlines* argument is equivalent to *text* and is provided for backwards compatibility. By default, file objects are opened in binary mode. @@ -1445,12 +1445,13 @@ This module also provides the following legacy functions from the 2.x none of the guarantees described above regarding security and exception handling consistency are valid for these functions. -.. function:: getstatusoutput(cmd) +.. function:: getstatusoutput(cmd, *, encoding=None, errors=None) Return ``(exitcode, output)`` of executing *cmd* in a shell. Execute the string *cmd* in a shell with :meth:`Popen.check_output` and - return a 2-tuple ``(exitcode, output)``. The locale encoding is used; + return a 2-tuple ``(exitcode, output)``. + *encoding* and *errors* are used to decode output; see the notes on :ref:`frequently-used-arguments` for more details. A trailing newline is stripped from the output. @@ -1475,8 +1476,10 @@ handling consistency are valid for these functions. as it did in Python 3.3.3 and earlier. exitcode has the same value as :attr:`~Popen.returncode`. + .. versionadded:: 3.11 + Added *encoding* and *errors* arguments. -.. function:: getoutput(cmd) +.. function:: getoutput(cmd, *, encoding=None, errors=None) Return output (stdout and stderr) of executing *cmd* in a shell. @@ -1491,6 +1494,9 @@ handling consistency are valid for these functions. .. versionchanged:: 3.3.4 Windows support added + .. versionadded:: 3.11 + Added *encoding* and *errors* arguments. + Notes ----- diff --git a/Lib/locale.py b/Lib/locale.py index 170e5ee..25eb75ac 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -655,6 +655,11 @@ try: except NameError: def getpreferredencoding(do_setlocale=True): """Return the charset that the user is likely using.""" + if sys.flags.warn_default_encoding: + import warnings + warnings.warn( + "UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead.", + EncodingWarning, 2) if sys.flags.utf8_mode: return 'utf-8' return getencoding() @@ -663,6 +668,12 @@ else: def getpreferredencoding(do_setlocale=True): """Return the charset that the user is likely using, according to the system configuration.""" + + if sys.flags.warn_default_encoding: + import warnings + warnings.warn( + "UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead.", + EncodingWarning, 2) if sys.flags.utf8_mode: return 'utf-8' diff --git a/Lib/subprocess.py b/Lib/subprocess.py index a5fa152..968cfc1 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -43,6 +43,7 @@ getstatusoutput(...): Runs a command in the shell, waits for it to complete, import builtins import errno import io +import locale import os import time import signal @@ -344,6 +345,26 @@ def _args_from_interpreter_flags(): return args +def _text_encoding(): + # Return default text encoding and emit EncodingWarning if + # sys.flags.warn_default_encoding is true. + if sys.flags.warn_default_encoding: + f = sys._getframe() + filename = f.f_code.co_filename + stacklevel = 2 + while f := f.f_back: + if f.f_code.co_filename != filename: + break + stacklevel += 1 + warnings.warn("'encoding' argument not specified.", + EncodingWarning, stacklevel) + + if sys.flags.utf8_mode: + return "utf-8" + else: + return locale.getencoding() + + def call(*popenargs, timeout=None, **kwargs): """Run command with arguments. Wait for command to complete or timeout, then return the returncode attribute. @@ -610,7 +631,7 @@ def list2cmdline(seq): # Various tools for executing commands and looking at their output and status. # -def getstatusoutput(cmd): +def getstatusoutput(cmd, *, encoding=None, errors=None): """Return (exitcode, output) of executing cmd in a shell. Execute the string 'cmd' in a shell with 'check_output' and @@ -632,7 +653,8 @@ def getstatusoutput(cmd): (-15, '') """ try: - data = check_output(cmd, shell=True, text=True, stderr=STDOUT) + data = check_output(cmd, shell=True, text=True, stderr=STDOUT, + encoding=encoding, errors=errors) exitcode = 0 except CalledProcessError as ex: data = ex.output @@ -641,7 +663,7 @@ def getstatusoutput(cmd): data = data[:-1] return exitcode, data -def getoutput(cmd): +def getoutput(cmd, *, encoding=None, errors=None): """Return output (stdout or stderr) of executing cmd in a shell. Like getstatusoutput(), except the exit status is ignored and the return @@ -651,7 +673,8 @@ def getoutput(cmd): >>> subprocess.getoutput('ls /bin/ls') '/bin/ls' """ - return getstatusoutput(cmd)[1] + return getstatusoutput(cmd, encoding=encoding, errors=errors)[1] + def _use_posix_spawn(): @@ -858,13 +881,8 @@ class Popen: errread = msvcrt.open_osfhandle(errread.Detach(), 0) self.text_mode = encoding or errors or text or universal_newlines - - # PEP 597: We suppress the EncodingWarning in subprocess module - # for now (at Python 3.10), because we focus on files for now. - # This will be changed to encoding = io.text_encoding(encoding) - # in the future. if self.text_mode and encoding is None: - self.encoding = encoding = "locale" + self.encoding = encoding = _text_encoding() # How long to resume waiting on a child after the first ^C. # There is no right value for this. The purpose is to be polite diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 99b5947..5814a6d 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1733,6 +1733,20 @@ class RunFuncTestCase(BaseTestCase): msg="TimeoutExpired was delayed! Bad traceback:\n```\n" f"{stacks}```") + def test_encoding_warning(self): + code = textwrap.dedent("""\ + from subprocess import * + args = ["echo", "hello"] + run(args, text=True) + check_output(args, text=True) + """) + cp = subprocess.run([sys.executable, "-Xwarn_default_encoding", "-c", code], + capture_output=True) + lines = cp.stderr.splitlines() + self.assertEqual(len(lines), 2) + self.assertTrue(lines[0].startswith(b"<string>:3: EncodingWarning: ")) + self.assertTrue(lines[1].startswith(b"<string>:4: EncodingWarning: ")) + def _get_test_grp_name(): for name_group in ('staff', 'nogroup', 'grp', 'nobody', 'nfsnobody'): diff --git a/Misc/NEWS.d/next/Library/2022-04-27-13-30-26.gh-issue-91954.cC7ga_.rst b/Misc/NEWS.d/next/Library/2022-04-27-13-30-26.gh-issue-91954.cC7ga_.rst new file mode 100644 index 0000000..b63db25 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-27-13-30-26.gh-issue-91954.cC7ga_.rst @@ -0,0 +1,2 @@ +Add *encoding* and *errors* arguments to :func:`subprocess.getoutput` and +:func:`subprocess.getstatusoutput`. |