https://github.com/python/cpython/commit/834ba5aaf21ac7fd123534dae8f9e478ee526aaa
commit: 834ba5aaf21ac7fd123534dae8f9e478ee526aaa
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-10-23T10:50:29+03:00
summary:
gh-58032: Deprecate the argparse.FileType type converter (GH-124664)
files:
A Misc/NEWS.d/next/Library/2024-09-27-13-10-17.gh-issue-58032.0aNAQ0.rst
M Doc/deprecations/pending-removal-in-future.rst
M Doc/library/argparse.rst
M Doc/whatsnew/3.14.rst
M Lib/argparse.py
M Lib/test/test_argparse.py
diff --git a/Doc/deprecations/pending-removal-in-future.rst
b/Doc/deprecations/pending-removal-in-future.rst
index d77fc86eab0ed6..5a4502ac08a5f0 100644
--- a/Doc/deprecations/pending-removal-in-future.rst
+++ b/Doc/deprecations/pending-removal-in-future.rst
@@ -4,16 +4,6 @@ Pending removal in future versions
The following APIs will be removed in the future,
although there is currently no date scheduled for their removal.
-* :mod:`argparse`:
-
- * Nesting argument groups and nesting mutually exclusive
- groups are deprecated.
- * Passing the undocumented keyword argument *prefix_chars* to
- :meth:`~argparse.ArgumentParser.add_argument_group` is now
- deprecated.
-
-* :mod:`array`'s ``'u'`` format code (:gh:`57281`)
-
* :mod:`builtins`:
* ``bool(NotImplemented)``.
@@ -43,6 +33,17 @@ although there is currently no date scheduled for their
removal.
as a single positional argument.
(Contributed by Serhiy Storchaka in :gh:`109218`.)
+* :mod:`argparse`:
+
+ * Nesting argument groups and nesting mutually exclusive
+ groups are deprecated.
+ * Passing the undocumented keyword argument *prefix_chars* to
+ :meth:`~argparse.ArgumentParser.add_argument_group` is now
+ deprecated.
+ * The :class:`argparse.FileType` type converter is deprecated.
+
+* :mod:`array`'s ``'u'`` format code (:gh:`57281`)
+
* :mod:`calendar`: ``calendar.January`` and ``calendar.February`` constants are
deprecated and replaced by :data:`calendar.JANUARY` and
:data:`calendar.FEBRUARY`.
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index ef0db3e9789c98..65663d43f50a9d 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -865,16 +865,14 @@ See also :ref:`specifying-ambiguous-arguments`. The
supported values are:
output files::
>>> parser = argparse.ArgumentParser()
- >>> parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
- ... default=sys.stdin)
- >>> parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'),
- ... default=sys.stdout)
+ >>> parser.add_argument('infile', nargs='?')
+ >>> parser.add_argument('outfile', nargs='?')
>>> parser.parse_args(['input.txt', 'output.txt'])
- Namespace(infile=<_io.TextIOWrapper name='input.txt' encoding='UTF-8'>,
- outfile=<_io.TextIOWrapper name='output.txt' encoding='UTF-8'>)
+ Namespace(infile='input.txt', outfile='output.txt')
+ >>> parser.parse_args(['input.txt'])
+ Namespace(infile='input.txt', outfile=None)
>>> parser.parse_args([])
- Namespace(infile=<_io.TextIOWrapper name='<stdin>' encoding='UTF-8'>,
- outfile=<_io.TextIOWrapper name='<stdout>' encoding='UTF-8'>)
+ Namespace(infile=None, outfile=None)
.. index:: single: * (asterisk); in argparse module
@@ -1033,7 +1031,6 @@ Common built-in types and functions can be used as type
converters:
parser.add_argument('distance', type=float)
parser.add_argument('street', type=ascii)
parser.add_argument('code_point', type=ord)
- parser.add_argument('dest_file', type=argparse.FileType('w',
encoding='latin-1'))
parser.add_argument('datapath', type=pathlib.Path)
User defined functions can be used as well:
@@ -1827,9 +1824,19 @@ FileType objects
>>> parser.parse_args(['-'])
Namespace(infile=<_io.TextIOWrapper name='<stdin>' encoding='UTF-8'>)
+ .. note::
+
+ If one argument uses *FileType* and then a subsequent argument fails,
+ an error is reported but the file is not automatically closed.
+ This can also clobber the output files.
+ In this case, it would be better to wait until after the parser has
+ run and then use the :keyword:`with`-statement to manage the files.
+
.. versionchanged:: 3.4
Added the *encodings* and *errors* parameters.
+ .. deprecated:: 3.14
+
Argument groups
^^^^^^^^^^^^^^^
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 1dd6c19018934b..b389e6da4c0ac3 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -464,6 +464,12 @@ Deprecated
as a single positional argument.
(Contributed by Serhiy Storchaka in :gh:`109218`.)
+* :mod:`argparse`:
+ Deprecated the :class:`argparse.FileType` type converter.
+ Anything with resource management should be done downstream after the
+ arguments are parsed.
+ (Contributed by Serhiy Storchaka in :gh:`58032`.)
+
* :mod:`multiprocessing` and :mod:`concurrent.futures`:
The default start method (see :ref:`multiprocessing-start-methods`) changed
away from *fork* to *forkserver* on platforms where it was not already
diff --git a/Lib/argparse.py b/Lib/argparse.py
index 024622bec17c3b..9746173984c6ca 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -18,11 +18,12 @@
'integers', metavar='int', nargs='+', type=int,
help='an integer to be summed')
parser.add_argument(
- '--log', default=sys.stdout, type=argparse.FileType('w'),
+ '--log',
help='the file where the sum should be written')
args = parser.parse_args()
- args.log.write('%s' % sum(args.integers))
- args.log.close()
+ with (open(args.log, 'w') if args.log is not None
+ else contextlib.nullcontext(sys.stdout)) as log:
+ log.write('%s' % sum(args.integers))
The module contains the following public classes:
@@ -39,7 +40,8 @@
- FileType -- A factory for defining types of files to be created. As the
example above shows, instances of FileType are typically passed as
- the type= argument of add_argument() calls.
+ the type= argument of add_argument() calls. Deprecated since
+ Python 3.14.
- Action -- The base class for parser actions. Typically actions are
selected by passing strings like 'store_true' or 'append_const' to
@@ -1252,7 +1254,7 @@ def __call__(self, parser, namespace, values,
option_string=None):
# ==============
class FileType(object):
- """Factory for creating file object types
+ """Deprecated factory for creating file object types
Instances of FileType are typically passed as type= arguments to the
ArgumentParser add_argument() method.
@@ -1269,6 +1271,12 @@ class FileType(object):
"""
def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None):
+ import warnings
+ warnings.warn(
+ "FileType is deprecated. Simply open files after parsing
arguments.",
+ category=PendingDeprecationWarning,
+ stacklevel=2
+ )
self._mode = mode
self._bufsize = bufsize
self._encoding = encoding
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 4bd7a935b9b757..ed1c5c34e526aa 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -1773,27 +1773,43 @@ def convert_arg_line_to_args(self, arg_line):
# Type conversion tests
# =====================
+def FileType(*args, **kwargs):
+ with warnings.catch_warnings():
+ warnings.filterwarnings('ignore', 'FileType is deprecated',
+ PendingDeprecationWarning, __name__)
+ return argparse.FileType(*args, **kwargs)
+
+
+class TestFileTypeDeprecation(TestCase):
+
+ def test(self):
+ with self.assertWarns(PendingDeprecationWarning) as cm:
+ argparse.FileType()
+ self.assertIn('FileType is deprecated', str(cm.warning))
+ self.assertEqual(cm.filename, __file__)
+
+
class TestFileTypeRepr(TestCase):
def test_r(self):
- type = argparse.FileType('r')
+ type = FileType('r')
self.assertEqual("FileType('r')", repr(type))
def test_wb_1(self):
- type = argparse.FileType('wb', 1)
+ type = FileType('wb', 1)
self.assertEqual("FileType('wb', 1)", repr(type))
def test_r_latin(self):
- type = argparse.FileType('r', encoding='latin_1')
+ type = FileType('r', encoding='latin_1')
self.assertEqual("FileType('r', encoding='latin_1')", repr(type))
def test_w_big5_ignore(self):
- type = argparse.FileType('w', encoding='big5', errors='ignore')
+ type = FileType('w', encoding='big5', errors='ignore')
self.assertEqual("FileType('w', encoding='big5', errors='ignore')",
repr(type))
def test_r_1_replace(self):
- type = argparse.FileType('r', 1, errors='replace')
+ type = FileType('r', 1, errors='replace')
self.assertEqual("FileType('r', 1, errors='replace')", repr(type))
@@ -1847,7 +1863,6 @@ def __eq__(self, other):
text = text.decode('ascii')
return self.name == other.name == text
-
class TestFileTypeR(TempDirMixin, ParserTestCase):
"""Test the FileType option/argument type for reading files"""
@@ -1860,8 +1875,8 @@ def setUp(self):
self.create_readonly_file('readonly')
argument_signatures = [
- Sig('-x', type=argparse.FileType()),
- Sig('spam', type=argparse.FileType('r')),
+ Sig('-x', type=FileType()),
+ Sig('spam', type=FileType('r')),
]
failures = ['-x', '', 'non-existent-file.txt']
successes = [
@@ -1881,7 +1896,7 @@ def setUp(self):
file.close()
argument_signatures = [
- Sig('-c', type=argparse.FileType('r'), default='no-file.txt'),
+ Sig('-c', type=FileType('r'), default='no-file.txt'),
]
# should provoke no such file error
failures = ['']
@@ -1900,8 +1915,8 @@ def setUp(self):
file.write(file_name)
argument_signatures = [
- Sig('-x', type=argparse.FileType('rb')),
- Sig('spam', type=argparse.FileType('rb')),
+ Sig('-x', type=FileType('rb')),
+ Sig('spam', type=FileType('rb')),
]
failures = ['-x', '']
successes = [
@@ -1939,8 +1954,8 @@ def setUp(self):
self.create_writable_file('writable')
argument_signatures = [
- Sig('-x', type=argparse.FileType('w')),
- Sig('spam', type=argparse.FileType('w')),
+ Sig('-x', type=FileType('w')),
+ Sig('spam', type=FileType('w')),
]
failures = ['-x', '', 'readonly']
successes = [
@@ -1962,8 +1977,8 @@ def setUp(self):
self.create_writable_file('writable')
argument_signatures = [
- Sig('-x', type=argparse.FileType('x')),
- Sig('spam', type=argparse.FileType('x')),
+ Sig('-x', type=FileType('x')),
+ Sig('spam', type=FileType('x')),
]
failures = ['-x', '', 'readonly', 'writable']
successes = [
@@ -1977,8 +1992,8 @@ class TestFileTypeWB(TempDirMixin, ParserTestCase):
"""Test the FileType option/argument type for writing binary files"""
argument_signatures = [
- Sig('-x', type=argparse.FileType('wb')),
- Sig('spam', type=argparse.FileType('wb')),
+ Sig('-x', type=FileType('wb')),
+ Sig('spam', type=FileType('wb')),
]
failures = ['-x', '']
successes = [
@@ -1994,8 +2009,8 @@ class TestFileTypeXB(TestFileTypeX):
"Test the FileType option/argument type for writing new binary files only"
argument_signatures = [
- Sig('-x', type=argparse.FileType('xb')),
- Sig('spam', type=argparse.FileType('xb')),
+ Sig('-x', type=FileType('xb')),
+ Sig('spam', type=FileType('xb')),
]
successes = [
('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))),
@@ -2007,7 +2022,7 @@ class TestFileTypeOpenArgs(TestCase):
"""Test that open (the builtin) is correctly called"""
def test_open_args(self):
- FT = argparse.FileType
+ FT = FileType
cases = [
(FT('rb'), ('rb', -1, None, None)),
(FT('w', 1), ('w', 1, None, None)),
@@ -2022,7 +2037,7 @@ def test_open_args(self):
def test_invalid_file_type(self):
with self.assertRaises(ValueError):
- argparse.FileType('b')('-test')
+ FileType('b')('-test')
class TestFileTypeMissingInitialization(TestCase):
diff --git
a/Misc/NEWS.d/next/Library/2024-09-27-13-10-17.gh-issue-58032.0aNAQ0.rst
b/Misc/NEWS.d/next/Library/2024-09-27-13-10-17.gh-issue-58032.0aNAQ0.rst
new file mode 100644
index 00000000000000..278512b22a8d3f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-27-13-10-17.gh-issue-58032.0aNAQ0.rst
@@ -0,0 +1 @@
+Deprecate the :class:`argparse.FileType` type converter.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]