mirror of
https://github.com/python/cpython.git
synced 2025-10-17 04:08:28 +00:00
gh-58032: Deprecate the argparse.FileType type converter (GH-124664)
This commit is contained in:
parent
c75ff2ef8e
commit
834ba5aaf2
6 changed files with 83 additions and 45 deletions
|
@ -4,16 +4,6 @@ Pending removal in future versions
|
||||||
The following APIs will be removed in the future,
|
The following APIs will be removed in the future,
|
||||||
although there is currently no date scheduled for their removal.
|
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`:
|
* :mod:`builtins`:
|
||||||
|
|
||||||
* ``bool(NotImplemented)``.
|
* ``bool(NotImplemented)``.
|
||||||
|
@ -43,6 +33,17 @@ although there is currently no date scheduled for their removal.
|
||||||
as a single positional argument.
|
as a single positional argument.
|
||||||
(Contributed by Serhiy Storchaka in :gh:`109218`.)
|
(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
|
* :mod:`calendar`: ``calendar.January`` and ``calendar.February`` constants are
|
||||||
deprecated and replaced by :data:`calendar.JANUARY` and
|
deprecated and replaced by :data:`calendar.JANUARY` and
|
||||||
:data:`calendar.FEBRUARY`.
|
:data:`calendar.FEBRUARY`.
|
||||||
|
|
|
@ -865,16 +865,14 @@ See also :ref:`specifying-ambiguous-arguments`. The supported values are:
|
||||||
output files::
|
output files::
|
||||||
|
|
||||||
>>> parser = argparse.ArgumentParser()
|
>>> parser = argparse.ArgumentParser()
|
||||||
>>> parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
|
>>> parser.add_argument('infile', nargs='?')
|
||||||
... default=sys.stdin)
|
>>> parser.add_argument('outfile', nargs='?')
|
||||||
>>> parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'),
|
|
||||||
... default=sys.stdout)
|
|
||||||
>>> parser.parse_args(['input.txt', 'output.txt'])
|
>>> parser.parse_args(['input.txt', 'output.txt'])
|
||||||
Namespace(infile=<_io.TextIOWrapper name='input.txt' encoding='UTF-8'>,
|
Namespace(infile='input.txt', outfile='output.txt')
|
||||||
outfile=<_io.TextIOWrapper name='output.txt' encoding='UTF-8'>)
|
>>> parser.parse_args(['input.txt'])
|
||||||
|
Namespace(infile='input.txt', outfile=None)
|
||||||
>>> parser.parse_args([])
|
>>> parser.parse_args([])
|
||||||
Namespace(infile=<_io.TextIOWrapper name='<stdin>' encoding='UTF-8'>,
|
Namespace(infile=None, outfile=None)
|
||||||
outfile=<_io.TextIOWrapper name='<stdout>' encoding='UTF-8'>)
|
|
||||||
|
|
||||||
.. index:: single: * (asterisk); in argparse module
|
.. 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('distance', type=float)
|
||||||
parser.add_argument('street', type=ascii)
|
parser.add_argument('street', type=ascii)
|
||||||
parser.add_argument('code_point', type=ord)
|
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)
|
parser.add_argument('datapath', type=pathlib.Path)
|
||||||
|
|
||||||
User defined functions can be used as well:
|
User defined functions can be used as well:
|
||||||
|
@ -1827,9 +1824,19 @@ FileType objects
|
||||||
>>> parser.parse_args(['-'])
|
>>> parser.parse_args(['-'])
|
||||||
Namespace(infile=<_io.TextIOWrapper name='<stdin>' encoding='UTF-8'>)
|
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
|
.. versionchanged:: 3.4
|
||||||
Added the *encodings* and *errors* parameters.
|
Added the *encodings* and *errors* parameters.
|
||||||
|
|
||||||
|
.. deprecated:: 3.14
|
||||||
|
|
||||||
|
|
||||||
Argument groups
|
Argument groups
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -464,6 +464,12 @@ Deprecated
|
||||||
as a single positional argument.
|
as a single positional argument.
|
||||||
(Contributed by Serhiy Storchaka in :gh:`109218`.)
|
(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`:
|
* :mod:`multiprocessing` and :mod:`concurrent.futures`:
|
||||||
The default start method (see :ref:`multiprocessing-start-methods`) changed
|
The default start method (see :ref:`multiprocessing-start-methods`) changed
|
||||||
away from *fork* to *forkserver* on platforms where it was not already
|
away from *fork* to *forkserver* on platforms where it was not already
|
||||||
|
|
|
@ -18,11 +18,12 @@ command-line and writes the result to a file::
|
||||||
'integers', metavar='int', nargs='+', type=int,
|
'integers', metavar='int', nargs='+', type=int,
|
||||||
help='an integer to be summed')
|
help='an integer to be summed')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--log', default=sys.stdout, type=argparse.FileType('w'),
|
'--log',
|
||||||
help='the file where the sum should be written')
|
help='the file where the sum should be written')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args.log.write('%s' % sum(args.integers))
|
with (open(args.log, 'w') if args.log is not None
|
||||||
args.log.close()
|
else contextlib.nullcontext(sys.stdout)) as log:
|
||||||
|
log.write('%s' % sum(args.integers))
|
||||||
|
|
||||||
The module contains the following public classes:
|
The module contains the following public classes:
|
||||||
|
|
||||||
|
@ -39,7 +40,8 @@ The module contains the following public classes:
|
||||||
|
|
||||||
- FileType -- A factory for defining types of files to be created. As the
|
- FileType -- A factory for defining types of files to be created. As the
|
||||||
example above shows, instances of FileType are typically passed as
|
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
|
- Action -- The base class for parser actions. Typically actions are
|
||||||
selected by passing strings like 'store_true' or 'append_const' to
|
selected by passing strings like 'store_true' or 'append_const' to
|
||||||
|
@ -1252,7 +1254,7 @@ class _ExtendAction(_AppendAction):
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
class FileType(object):
|
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
|
Instances of FileType are typically passed as type= arguments to the
|
||||||
ArgumentParser add_argument() method.
|
ArgumentParser add_argument() method.
|
||||||
|
@ -1269,6 +1271,12 @@ class FileType(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None):
|
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._mode = mode
|
||||||
self._bufsize = bufsize
|
self._bufsize = bufsize
|
||||||
self._encoding = encoding
|
self._encoding = encoding
|
||||||
|
|
|
@ -1773,27 +1773,43 @@ class TestArgumentsFromFileConverter(TempDirMixin, ParserTestCase):
|
||||||
# Type conversion tests
|
# 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):
|
class TestFileTypeRepr(TestCase):
|
||||||
|
|
||||||
def test_r(self):
|
def test_r(self):
|
||||||
type = argparse.FileType('r')
|
type = FileType('r')
|
||||||
self.assertEqual("FileType('r')", repr(type))
|
self.assertEqual("FileType('r')", repr(type))
|
||||||
|
|
||||||
def test_wb_1(self):
|
def test_wb_1(self):
|
||||||
type = argparse.FileType('wb', 1)
|
type = FileType('wb', 1)
|
||||||
self.assertEqual("FileType('wb', 1)", repr(type))
|
self.assertEqual("FileType('wb', 1)", repr(type))
|
||||||
|
|
||||||
def test_r_latin(self):
|
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))
|
self.assertEqual("FileType('r', encoding='latin_1')", repr(type))
|
||||||
|
|
||||||
def test_w_big5_ignore(self):
|
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')",
|
self.assertEqual("FileType('w', encoding='big5', errors='ignore')",
|
||||||
repr(type))
|
repr(type))
|
||||||
|
|
||||||
def test_r_1_replace(self):
|
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))
|
self.assertEqual("FileType('r', 1, errors='replace')", repr(type))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1847,7 +1863,6 @@ class RFile(object):
|
||||||
text = text.decode('ascii')
|
text = text.decode('ascii')
|
||||||
return self.name == other.name == text
|
return self.name == other.name == text
|
||||||
|
|
||||||
|
|
||||||
class TestFileTypeR(TempDirMixin, ParserTestCase):
|
class TestFileTypeR(TempDirMixin, ParserTestCase):
|
||||||
"""Test the FileType option/argument type for reading files"""
|
"""Test the FileType option/argument type for reading files"""
|
||||||
|
|
||||||
|
@ -1860,8 +1875,8 @@ class TestFileTypeR(TempDirMixin, ParserTestCase):
|
||||||
self.create_readonly_file('readonly')
|
self.create_readonly_file('readonly')
|
||||||
|
|
||||||
argument_signatures = [
|
argument_signatures = [
|
||||||
Sig('-x', type=argparse.FileType()),
|
Sig('-x', type=FileType()),
|
||||||
Sig('spam', type=argparse.FileType('r')),
|
Sig('spam', type=FileType('r')),
|
||||||
]
|
]
|
||||||
failures = ['-x', '', 'non-existent-file.txt']
|
failures = ['-x', '', 'non-existent-file.txt']
|
||||||
successes = [
|
successes = [
|
||||||
|
@ -1881,7 +1896,7 @@ class TestFileTypeDefaults(TempDirMixin, ParserTestCase):
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
argument_signatures = [
|
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
|
# should provoke no such file error
|
||||||
failures = ['']
|
failures = ['']
|
||||||
|
@ -1900,8 +1915,8 @@ class TestFileTypeRB(TempDirMixin, ParserTestCase):
|
||||||
file.write(file_name)
|
file.write(file_name)
|
||||||
|
|
||||||
argument_signatures = [
|
argument_signatures = [
|
||||||
Sig('-x', type=argparse.FileType('rb')),
|
Sig('-x', type=FileType('rb')),
|
||||||
Sig('spam', type=argparse.FileType('rb')),
|
Sig('spam', type=FileType('rb')),
|
||||||
]
|
]
|
||||||
failures = ['-x', '']
|
failures = ['-x', '']
|
||||||
successes = [
|
successes = [
|
||||||
|
@ -1939,8 +1954,8 @@ class TestFileTypeW(TempDirMixin, ParserTestCase):
|
||||||
self.create_writable_file('writable')
|
self.create_writable_file('writable')
|
||||||
|
|
||||||
argument_signatures = [
|
argument_signatures = [
|
||||||
Sig('-x', type=argparse.FileType('w')),
|
Sig('-x', type=FileType('w')),
|
||||||
Sig('spam', type=argparse.FileType('w')),
|
Sig('spam', type=FileType('w')),
|
||||||
]
|
]
|
||||||
failures = ['-x', '', 'readonly']
|
failures = ['-x', '', 'readonly']
|
||||||
successes = [
|
successes = [
|
||||||
|
@ -1962,8 +1977,8 @@ class TestFileTypeX(TempDirMixin, ParserTestCase):
|
||||||
self.create_writable_file('writable')
|
self.create_writable_file('writable')
|
||||||
|
|
||||||
argument_signatures = [
|
argument_signatures = [
|
||||||
Sig('-x', type=argparse.FileType('x')),
|
Sig('-x', type=FileType('x')),
|
||||||
Sig('spam', type=argparse.FileType('x')),
|
Sig('spam', type=FileType('x')),
|
||||||
]
|
]
|
||||||
failures = ['-x', '', 'readonly', 'writable']
|
failures = ['-x', '', 'readonly', 'writable']
|
||||||
successes = [
|
successes = [
|
||||||
|
@ -1977,8 +1992,8 @@ class TestFileTypeWB(TempDirMixin, ParserTestCase):
|
||||||
"""Test the FileType option/argument type for writing binary files"""
|
"""Test the FileType option/argument type for writing binary files"""
|
||||||
|
|
||||||
argument_signatures = [
|
argument_signatures = [
|
||||||
Sig('-x', type=argparse.FileType('wb')),
|
Sig('-x', type=FileType('wb')),
|
||||||
Sig('spam', type=argparse.FileType('wb')),
|
Sig('spam', type=FileType('wb')),
|
||||||
]
|
]
|
||||||
failures = ['-x', '']
|
failures = ['-x', '']
|
||||||
successes = [
|
successes = [
|
||||||
|
@ -1994,8 +2009,8 @@ class TestFileTypeXB(TestFileTypeX):
|
||||||
"Test the FileType option/argument type for writing new binary files only"
|
"Test the FileType option/argument type for writing new binary files only"
|
||||||
|
|
||||||
argument_signatures = [
|
argument_signatures = [
|
||||||
Sig('-x', type=argparse.FileType('xb')),
|
Sig('-x', type=FileType('xb')),
|
||||||
Sig('spam', type=argparse.FileType('xb')),
|
Sig('spam', type=FileType('xb')),
|
||||||
]
|
]
|
||||||
successes = [
|
successes = [
|
||||||
('-x foo bar', NS(x=WFile('foo'), spam=WFile('bar'))),
|
('-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"""
|
"""Test that open (the builtin) is correctly called"""
|
||||||
|
|
||||||
def test_open_args(self):
|
def test_open_args(self):
|
||||||
FT = argparse.FileType
|
FT = FileType
|
||||||
cases = [
|
cases = [
|
||||||
(FT('rb'), ('rb', -1, None, None)),
|
(FT('rb'), ('rb', -1, None, None)),
|
||||||
(FT('w', 1), ('w', 1, None, None)),
|
(FT('w', 1), ('w', 1, None, None)),
|
||||||
|
@ -2022,7 +2037,7 @@ class TestFileTypeOpenArgs(TestCase):
|
||||||
|
|
||||||
def test_invalid_file_type(self):
|
def test_invalid_file_type(self):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
argparse.FileType('b')('-test')
|
FileType('b')('-test')
|
||||||
|
|
||||||
|
|
||||||
class TestFileTypeMissingInitialization(TestCase):
|
class TestFileTypeMissingInitialization(TestCase):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Deprecate the :class:`argparse.FileType` type converter.
|
Loading…
Add table
Add a link
Reference in a new issue