[Python-checkins] gh-104773: PEP 594: Remove the pipes module (#104848)

vstinner webhook-mailer at python.org
Wed May 24 07:11:37 EDT 2023


https://github.com/python/cpython/commit/a4b7e9d1f812f2598ac9637d95e986c830bd451b
commit: a4b7e9d1f812f2598ac9637d95e986c830bd451b
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2023-05-24T13:11:29+02:00
summary:

gh-104773: PEP 594: Remove the pipes module (#104848)

files:
A Misc/NEWS.d/next/Library/2023-05-24-11-45-22.gh-issue-104773.R0Br4-.rst
D Doc/library/pipes.rst
D Lib/pipes.py
D Lib/test/test_pipes.py
M Doc/library/superseded.rst
M Doc/whatsnew/3.11.rst
M Doc/whatsnew/3.12.rst
M Doc/whatsnew/3.13.rst
M Doc/whatsnew/3.3.rst
M Python/stdlib_module_names.h

diff --git a/Doc/library/pipes.rst b/Doc/library/pipes.rst
deleted file mode 100644
index 471ae0dbc976..000000000000
--- a/Doc/library/pipes.rst
+++ /dev/null
@@ -1,103 +0,0 @@
-:mod:`pipes` --- Interface to shell pipelines
-=============================================
-
-.. module:: pipes
-   :platform: Unix
-   :synopsis: A Python interface to Unix shell pipelines.
-   :deprecated:
-
-.. sectionauthor:: Moshe Zadka <moshez at zadka.site.co.il>
-
-**Source code:** :source:`Lib/pipes.py`
-
-.. deprecated-removed:: 3.11 3.13
-   The :mod:`pipes` module is deprecated
-   (see :pep:`PEP 594 <594#pipes>` for details).
-   Please use the :mod:`subprocess` module instead.
-
---------------
-
-The :mod:`pipes` module defines a class to abstract the concept of a *pipeline*
---- a sequence of converters from one file to  another.
-
-Because the module uses :program:`/bin/sh` command lines, a POSIX or compatible
-shell for :func:`os.system` and :func:`os.popen` is required.
-
-.. availability:: Unix, not VxWorks.
-
-The :mod:`pipes` module defines the following class:
-
-
-.. class:: Template()
-
-   An abstraction of a pipeline.
-
-Example::
-
-   >>> import pipes
-   >>> t = pipes.Template()
-   >>> t.append('tr a-z A-Z', '--')
-   >>> f = t.open('pipefile', 'w')
-   >>> f.write('hello world')
-   >>> f.close()
-   >>> open('pipefile').read()
-   'HELLO WORLD'
-
-
-.. _template-objects:
-
-Template Objects
-----------------
-
-Template objects following methods:
-
-
-.. method:: Template.reset()
-
-   Restore a pipeline template to its initial state.
-
-
-.. method:: Template.clone()
-
-   Return a new, equivalent, pipeline template.
-
-
-.. method:: Template.debug(flag)
-
-   If *flag* is true, turn debugging on. Otherwise, turn debugging off. When
-   debugging is on, commands to be executed are printed, and the shell is given
-   ``set -x`` command to be more verbose.
-
-
-.. method:: Template.append(cmd, kind)
-
-   Append a new action at the end. The *cmd* variable must be a valid bourne shell
-   command. The *kind* variable consists of two letters.
-
-   The first letter can be either of ``'-'`` (which means the command reads its
-   standard input), ``'f'`` (which means the commands reads a given file on the
-   command line) or ``'.'`` (which means the commands reads no input, and hence
-   must be first.)
-
-   Similarly, the second letter can be either of ``'-'`` (which means  the command
-   writes to standard output), ``'f'`` (which means the  command writes a file on
-   the command line) or ``'.'`` (which means the command does not write anything,
-   and hence must be last.)
-
-
-.. method:: Template.prepend(cmd, kind)
-
-   Add a new action at the beginning. See :meth:`append` for explanations of the
-   arguments.
-
-
-.. method:: Template.open(file, mode)
-
-   Return a file-like object, open to *file*, but read from or written to by the
-   pipeline.  Note that only one of ``'r'``, ``'w'`` may be given.
-
-
-.. method:: Template.copy(infile, outfile)
-
-   Copy *infile* to *outfile* through the pipe.
-
diff --git a/Doc/library/superseded.rst b/Doc/library/superseded.rst
index cad4e9c50b69..0153b4550558 100644
--- a/Doc/library/superseded.rst
+++ b/Doc/library/superseded.rst
@@ -21,7 +21,6 @@ backwards compatibility. They have been superseded by other modules.
    nntplib.rst
    optparse.rst
    ossaudiodev.rst
-   pipes.rst
    spwd.rst
    sunau.rst
    uu.rst
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 4ad68463e0b5..778ab28419ce 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -1731,7 +1731,7 @@ Modules
   slated for removal in Python 3.13:
 
   +---------------------+---------------------+---------------------+---------------------+---------------------+
-  | :mod:`aifc`         | :mod:`chunk`        | :mod:`msilib`       | :mod:`pipes`        | :mod:`!telnetlib`   |
+  | :mod:`aifc`         | :mod:`chunk`        | :mod:`msilib`       | :mod:`!pipes`       | :mod:`!telnetlib`   |
   +---------------------+---------------------+---------------------+---------------------+---------------------+
   | :mod:`audioop`      | :mod:`crypt`        | :mod:`nis`          | :mod:`!sndhdr`      | :mod:`uu`           |
   +---------------------+---------------------+---------------------+---------------------+---------------------+
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 9cf10196dcfc..62050aa01461 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -898,7 +898,7 @@ Modules (see :pep:`594`):
 * :mod:`nis`
 * :mod:`nntplib`
 * :mod:`ossaudiodev`
-* :mod:`pipes`
+* :mod:`!pipes`
 * :mod:`!sndhdr`
 * :mod:`spwd`
 * :mod:`sunau`
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index 1d4a4ca9614c..a5549f4470d2 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -153,6 +153,10 @@ Removed
   <https://pypi.org/project/python-magic/>`_ instead.
   (Contributed by Victor Stinner in :gh:`104773`.)
 
+* :pep:`594`: Remove the :mod:`!pipes` module, deprecated in Python 3.11:
+  use the :mod:`subprocess` module instead.
+  (Contributed by Victor Stinner in :gh:`104773`.)
+
 
 Porting to Python 3.13
 ======================
diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
index f121652ba51c..c05b8e5ef753 100644
--- a/Doc/whatsnew/3.3.rst
+++ b/Doc/whatsnew/3.3.rst
@@ -1778,7 +1778,7 @@ shlex
 -----
 
 The previously undocumented helper function ``quote`` from the
-:mod:`pipes` modules has been moved to the :mod:`shlex` module and
+:mod:`!pipes` modules has been moved to the :mod:`shlex` module and
 documented.  :func:`~shlex.quote` properly escapes all characters in a string
 that might be otherwise given special meaning by the shell.
 
diff --git a/Lib/pipes.py b/Lib/pipes.py
deleted file mode 100644
index 61d63b48d3e4..000000000000
--- a/Lib/pipes.py
+++ /dev/null
@@ -1,250 +0,0 @@
-"""Conversion pipeline templates.
-
-The problem:
-------------
-
-Suppose you have some data that you want to convert to another format,
-such as from GIF image format to PPM image format.  Maybe the
-conversion involves several steps (e.g. piping it through compress or
-uuencode).  Some of the conversion steps may require that their input
-is a disk file, others may be able to read standard input; similar for
-their output.  The input to the entire conversion may also be read
-from a disk file or from an open file, and similar for its output.
-
-The module lets you construct a pipeline template by sticking one or
-more conversion steps together.  It will take care of creating and
-removing temporary files if they are necessary to hold intermediate
-data.  You can then use the template to do conversions from many
-different sources to many different destinations.  The temporary
-file names used are different each time the template is used.
-
-The templates are objects so you can create templates for many
-different conversion steps and store them in a dictionary, for
-instance.
-
-
-Directions:
------------
-
-To create a template:
-    t = Template()
-
-To add a conversion step to a template:
-   t.append(command, kind)
-where kind is a string of two characters: the first is '-' if the
-command reads its standard input or 'f' if it requires a file; the
-second likewise for the output. The command must be valid /bin/sh
-syntax.  If input or output files are required, they are passed as
-$IN and $OUT; otherwise, it must be  possible to use the command in
-a pipeline.
-
-To add a conversion step at the beginning:
-   t.prepend(command, kind)
-
-To convert a file to another file using a template:
-  sts = t.copy(infile, outfile)
-If infile or outfile are the empty string, standard input is read or
-standard output is written, respectively.  The return value is the
-exit status of the conversion pipeline.
-
-To open a file for reading or writing through a conversion pipeline:
-   fp = t.open(file, mode)
-where mode is 'r' to read the file, or 'w' to write it -- just like
-for the built-in function open() or for os.popen().
-
-To create a new template object initialized to a given one:
-   t2 = t.clone()
-"""                                     # '
-
-
-import re
-import os
-import tempfile
-import warnings
-# we import the quote function rather than the module for backward compat
-# (quote used to be an undocumented but used function in pipes)
-from shlex import quote
-
-warnings._deprecated(__name__, remove=(3, 13))
-
-__all__ = ["Template"]
-
-# Conversion step kinds
-
-FILEIN_FILEOUT = 'ff'                   # Must read & write real files
-STDIN_FILEOUT  = '-f'                   # Must write a real file
-FILEIN_STDOUT  = 'f-'                   # Must read a real file
-STDIN_STDOUT   = '--'                   # Normal pipeline element
-SOURCE         = '.-'                   # Must be first, writes stdout
-SINK           = '-.'                   # Must be last, reads stdin
-
-stepkinds = [FILEIN_FILEOUT, STDIN_FILEOUT, FILEIN_STDOUT, STDIN_STDOUT, \
-             SOURCE, SINK]
-
-
-class Template:
-    """Class representing a pipeline template."""
-
-    def __init__(self):
-        """Template() returns a fresh pipeline template."""
-        self.debugging = 0
-        self.reset()
-
-    def __repr__(self):
-        """t.__repr__() implements repr(t)."""
-        return '<Template instance, steps=%r>' % (self.steps,)
-
-    def reset(self):
-        """t.reset() restores a pipeline template to its initial state."""
-        self.steps = []
-
-    def clone(self):
-        """t.clone() returns a new pipeline template with identical
-        initial state as the current one."""
-        t = Template()
-        t.steps = self.steps[:]
-        t.debugging = self.debugging
-        return t
-
-    def debug(self, flag):
-        """t.debug(flag) turns debugging on or off."""
-        self.debugging = flag
-
-    def append(self, cmd, kind):
-        """t.append(cmd, kind) adds a new step at the end."""
-        if not isinstance(cmd, str):
-            raise TypeError('Template.append: cmd must be a string')
-        if kind not in stepkinds:
-            raise ValueError('Template.append: bad kind %r' % (kind,))
-        if kind == SOURCE:
-            raise ValueError('Template.append: SOURCE can only be prepended')
-        if self.steps and self.steps[-1][1] == SINK:
-            raise ValueError('Template.append: already ends with SINK')
-        if kind[0] == 'f' and not re.search(r'\$IN\b', cmd):
-            raise ValueError('Template.append: missing $IN in cmd')
-        if kind[1] == 'f' and not re.search(r'\$OUT\b', cmd):
-            raise ValueError('Template.append: missing $OUT in cmd')
-        self.steps.append((cmd, kind))
-
-    def prepend(self, cmd, kind):
-        """t.prepend(cmd, kind) adds a new step at the front."""
-        if not isinstance(cmd, str):
-            raise TypeError('Template.prepend: cmd must be a string')
-        if kind not in stepkinds:
-            raise ValueError('Template.prepend: bad kind %r' % (kind,))
-        if kind == SINK:
-            raise ValueError('Template.prepend: SINK can only be appended')
-        if self.steps and self.steps[0][1] == SOURCE:
-            raise ValueError('Template.prepend: already begins with SOURCE')
-        if kind[0] == 'f' and not re.search(r'\$IN\b', cmd):
-            raise ValueError('Template.prepend: missing $IN in cmd')
-        if kind[1] == 'f' and not re.search(r'\$OUT\b', cmd):
-            raise ValueError('Template.prepend: missing $OUT in cmd')
-        self.steps.insert(0, (cmd, kind))
-
-    def open(self, file, rw):
-        """t.open(file, rw) returns a pipe or file object open for
-        reading or writing; the file is the other end of the pipeline."""
-        if rw == 'r':
-            return self.open_r(file)
-        if rw == 'w':
-            return self.open_w(file)
-        raise ValueError('Template.open: rw must be \'r\' or \'w\', not %r'
-                         % (rw,))
-
-    def open_r(self, file):
-        """t.open_r(file) and t.open_w(file) implement
-        t.open(file, 'r') and t.open(file, 'w') respectively."""
-        if not self.steps:
-            return open(file, 'r')
-        if self.steps[-1][1] == SINK:
-            raise ValueError('Template.open_r: pipeline ends width SINK')
-        cmd = self.makepipeline(file, '')
-        return os.popen(cmd, 'r')
-
-    def open_w(self, file):
-        if not self.steps:
-            return open(file, 'w')
-        if self.steps[0][1] == SOURCE:
-            raise ValueError('Template.open_w: pipeline begins with SOURCE')
-        cmd = self.makepipeline('', file)
-        return os.popen(cmd, 'w')
-
-    def copy(self, infile, outfile):
-        return os.system(self.makepipeline(infile, outfile))
-
-    def makepipeline(self, infile, outfile):
-        cmd = makepipeline(infile, self.steps, outfile)
-        if self.debugging:
-            print(cmd)
-            cmd = 'set -x; ' + cmd
-        return cmd
-
-
-def makepipeline(infile, steps, outfile):
-    # Build a list with for each command:
-    # [input filename or '', command string, kind, output filename or '']
-
-    list = []
-    for cmd, kind in steps:
-        list.append(['', cmd, kind, ''])
-    #
-    # Make sure there is at least one step
-    #
-    if not list:
-        list.append(['', 'cat', '--', ''])
-    #
-    # Take care of the input and output ends
-    #
-    [cmd, kind] = list[0][1:3]
-    if kind[0] == 'f' and not infile:
-        list.insert(0, ['', 'cat', '--', ''])
-    list[0][0] = infile
-    #
-    [cmd, kind] = list[-1][1:3]
-    if kind[1] == 'f' and not outfile:
-        list.append(['', 'cat', '--', ''])
-    list[-1][-1] = outfile
-    #
-    # Invent temporary files to connect stages that need files
-    #
-    garbage = []
-    for i in range(1, len(list)):
-        lkind = list[i-1][2]
-        rkind = list[i][2]
-        if lkind[1] == 'f' or rkind[0] == 'f':
-            (fd, temp) = tempfile.mkstemp()
-            os.close(fd)
-            garbage.append(temp)
-            list[i-1][-1] = list[i][0] = temp
-    #
-    for item in list:
-        [inf, cmd, kind, outf] = item
-        if kind[1] == 'f':
-            cmd = 'OUT=' + quote(outf) + '; ' + cmd
-        if kind[0] == 'f':
-            cmd = 'IN=' + quote(inf) + '; ' + cmd
-        if kind[0] == '-' and inf:
-            cmd = cmd + ' <' + quote(inf)
-        if kind[1] == '-' and outf:
-            cmd = cmd + ' >' + quote(outf)
-        item[1] = cmd
-    #
-    cmdlist = list[0][1]
-    for item in list[1:]:
-        [cmd, kind] = item[1:3]
-        if item[0] == '':
-            if 'f' in kind:
-                cmd = '{ ' + cmd + '; }'
-            cmdlist = cmdlist + ' |\n' + cmd
-        else:
-            cmdlist = cmdlist + '\n' + cmd
-    #
-    if garbage:
-        rmcmd = 'rm -f'
-        for file in garbage:
-            rmcmd = rmcmd + ' ' + quote(file)
-        trapcmd = 'trap ' + quote(rmcmd + '; exit') + ' 1 2 3 13 14 15'
-        cmdlist = trapcmd + '\n' + cmdlist + '\n' + rmcmd
-    #
-    return cmdlist
diff --git a/Lib/test/test_pipes.py b/Lib/test/test_pipes.py
deleted file mode 100644
index 09e21153ec85..000000000000
--- a/Lib/test/test_pipes.py
+++ /dev/null
@@ -1,210 +0,0 @@
-import os
-import string
-import unittest
-import shutil
-from test.support import reap_children, unix_shell
-from test.support.os_helper import TESTFN, unlink
-from test.support.warnings_helper import import_deprecated
-
-pipes = import_deprecated("pipes")
-
-
-if os.name != 'posix':
-    raise unittest.SkipTest('pipes module only works on posix')
-
-if not (unix_shell and os.path.exists(unix_shell)):
-    raise unittest.SkipTest('pipes module requires a shell')
-
-TESTFN2 = TESTFN + "2"
-
-# tr a-z A-Z is not portable, so make the ranges explicit
-s_command = 'tr %s %s' % (string.ascii_lowercase, string.ascii_uppercase)
-
-class SimplePipeTests(unittest.TestCase):
-    def tearDown(self):
-        for f in (TESTFN, TESTFN2):
-            unlink(f)
-
-    def testSimplePipe1(self):
-        if shutil.which('tr') is None:
-            self.skipTest('tr is not available')
-        t = pipes.Template()
-        t.append(s_command, pipes.STDIN_STDOUT)
-        with t.open(TESTFN, 'w') as f:
-            f.write('hello world #1')
-        with open(TESTFN) as f:
-            self.assertEqual(f.read(), 'HELLO WORLD #1')
-
-    def testSimplePipe2(self):
-        if shutil.which('tr') is None:
-            self.skipTest('tr is not available')
-        with open(TESTFN, 'w') as f:
-            f.write('hello world #2')
-        t = pipes.Template()
-        t.append(s_command + ' < $IN > $OUT', pipes.FILEIN_FILEOUT)
-        t.copy(TESTFN, TESTFN2)
-        with open(TESTFN2) as f:
-            self.assertEqual(f.read(), 'HELLO WORLD #2')
-
-    def testSimplePipe3(self):
-        if shutil.which('tr') is None:
-            self.skipTest('tr is not available')
-        with open(TESTFN, 'w') as f:
-            f.write('hello world #2')
-        t = pipes.Template()
-        t.append(s_command + ' < $IN', pipes.FILEIN_STDOUT)
-        f = t.open(TESTFN, 'r')
-        try:
-            self.assertEqual(f.read(), 'HELLO WORLD #2')
-        finally:
-            f.close()
-
-    def testEmptyPipeline1(self):
-        # copy through empty pipe
-        d = 'empty pipeline test COPY'
-        with open(TESTFN, 'w') as f:
-            f.write(d)
-        with open(TESTFN2, 'w') as f:
-            f.write('')
-        t=pipes.Template()
-        t.copy(TESTFN, TESTFN2)
-        with open(TESTFN2) as f:
-            self.assertEqual(f.read(), d)
-
-    def testEmptyPipeline2(self):
-        # read through empty pipe
-        d = 'empty pipeline test READ'
-        with open(TESTFN, 'w') as f:
-            f.write(d)
-        t=pipes.Template()
-        f = t.open(TESTFN, 'r')
-        try:
-            self.assertEqual(f.read(), d)
-        finally:
-            f.close()
-
-    def testEmptyPipeline3(self):
-        # write through empty pipe
-        d = 'empty pipeline test WRITE'
-        t = pipes.Template()
-        with t.open(TESTFN, 'w') as f:
-            f.write(d)
-        with open(TESTFN) as f:
-            self.assertEqual(f.read(), d)
-
-    def testRepr(self):
-        t = pipes.Template()
-        self.assertEqual(repr(t), "<Template instance, steps=[]>")
-        t.append('tr a-z A-Z', pipes.STDIN_STDOUT)
-        self.assertEqual(repr(t),
-                    "<Template instance, steps=[('tr a-z A-Z', '--')]>")
-
-    def testSetDebug(self):
-        t = pipes.Template()
-        t.debug(False)
-        self.assertEqual(t.debugging, False)
-        t.debug(True)
-        self.assertEqual(t.debugging, True)
-
-    def testReadOpenSink(self):
-        # check calling open('r') on a pipe ending with
-        # a sink raises ValueError
-        t = pipes.Template()
-        t.append('boguscmd', pipes.SINK)
-        self.assertRaises(ValueError, t.open, 'bogusfile', 'r')
-
-    def testWriteOpenSource(self):
-        # check calling open('w') on a pipe ending with
-        # a source raises ValueError
-        t = pipes.Template()
-        t.prepend('boguscmd', pipes.SOURCE)
-        self.assertRaises(ValueError, t.open, 'bogusfile', 'w')
-
-    def testBadAppendOptions(self):
-        t = pipes.Template()
-
-        # try a non-string command
-        self.assertRaises(TypeError, t.append, 7, pipes.STDIN_STDOUT)
-
-        # try a type that isn't recognized
-        self.assertRaises(ValueError, t.append, 'boguscmd', 'xx')
-
-        # shouldn't be able to append a source
-        self.assertRaises(ValueError, t.append, 'boguscmd', pipes.SOURCE)
-
-        # check appending two sinks
-        t = pipes.Template()
-        t.append('boguscmd', pipes.SINK)
-        self.assertRaises(ValueError, t.append, 'boguscmd', pipes.SINK)
-
-        # command needing file input but with no $IN
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.append, 'boguscmd $OUT',
-                           pipes.FILEIN_FILEOUT)
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.append, 'boguscmd',
-                           pipes.FILEIN_STDOUT)
-
-        # command needing file output but with no $OUT
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.append, 'boguscmd $IN',
-                           pipes.FILEIN_FILEOUT)
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.append, 'boguscmd',
-                           pipes.STDIN_FILEOUT)
-
-
-    def testBadPrependOptions(self):
-        t = pipes.Template()
-
-        # try a non-string command
-        self.assertRaises(TypeError, t.prepend, 7, pipes.STDIN_STDOUT)
-
-        # try a type that isn't recognized
-        self.assertRaises(ValueError, t.prepend, 'tr a-z A-Z', 'xx')
-
-        # shouldn't be able to prepend a sink
-        self.assertRaises(ValueError, t.prepend, 'boguscmd', pipes.SINK)
-
-        # check prepending two sources
-        t = pipes.Template()
-        t.prepend('boguscmd', pipes.SOURCE)
-        self.assertRaises(ValueError, t.prepend, 'boguscmd', pipes.SOURCE)
-
-        # command needing file input but with no $IN
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.prepend, 'boguscmd $OUT',
-                           pipes.FILEIN_FILEOUT)
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.prepend, 'boguscmd',
-                           pipes.FILEIN_STDOUT)
-
-        # command needing file output but with no $OUT
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.prepend, 'boguscmd $IN',
-                           pipes.FILEIN_FILEOUT)
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.prepend, 'boguscmd',
-                           pipes.STDIN_FILEOUT)
-
-    def testBadOpenMode(self):
-        t = pipes.Template()
-        self.assertRaises(ValueError, t.open, 'bogusfile', 'x')
-
-    def testClone(self):
-        t = pipes.Template()
-        t.append('tr a-z A-Z', pipes.STDIN_STDOUT)
-
-        u = t.clone()
-        self.assertNotEqual(id(t), id(u))
-        self.assertEqual(t.steps, u.steps)
-        self.assertNotEqual(id(t.steps), id(u.steps))
-        self.assertEqual(t.debugging, u.debugging)
-
-
-def tearDownModule():
-    reap_children()
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2023-05-24-11-45-22.gh-issue-104773.R0Br4-.rst b/Misc/NEWS.d/next/Library/2023-05-24-11-45-22.gh-issue-104773.R0Br4-.rst
new file mode 100644
index 000000000000..31da29ec2a59
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-05-24-11-45-22.gh-issue-104773.R0Br4-.rst
@@ -0,0 +1,2 @@
+:pep:`594`: Remove the :mod:`!pipes` module, deprecated in Python 3.11.
+Patch by Victor Stinner.
diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h
index 245c3a1645f3..c82512f43c7e 100644
--- a/Python/stdlib_module_names.h
+++ b/Python/stdlib_module_names.h
@@ -200,7 +200,6 @@ static const char* _Py_stdlib_module_names[] = {
 "pdb",
 "pickle",
 "pickletools",
-"pipes",
 "pkgutil",
 "platform",
 "plistlib",



More information about the Python-checkins mailing list