mirror of https://github.com/python/cpython
gh-58032: Deprecate the argparse.FileType type converter (GH-124664)
This commit is contained in:
parent
c75ff2ef8e
commit
834ba5aaf2
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -18,11 +18,12 @@ command-line and writes the result to a file::
|
|||
'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 @@ The module contains the following public classes:
|
|||
|
||||
- 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 @@ class _ExtendAction(_AppendAction):
|
|||
# ==============
|
||||
|
||||
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
|
||||
|
|
|
@ -1773,27 +1773,43 @@ class TestArgumentsFromFileConverter(TempDirMixin, ParserTestCase):
|
|||
# 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 @@ class RFile(object):
|
|||
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 @@ class TestFileTypeR(TempDirMixin, ParserTestCase):
|
|||
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 @@ class TestFileTypeDefaults(TempDirMixin, ParserTestCase):
|
|||
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 @@ class TestFileTypeRB(TempDirMixin, ParserTestCase):
|
|||
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 @@ class TestFileTypeW(TempDirMixin, ParserTestCase):
|
|||
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 @@ class TestFileTypeX(TempDirMixin, ParserTestCase):
|
|||
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 @@ class TestFileTypeOpenArgs(TestCase):
|
|||
|
||||
def test_invalid_file_type(self):
|
||||
with self.assertRaises(ValueError):
|
||||
argparse.FileType('b')('-test')
|
||||
FileType('b')('-test')
|
||||
|
||||
|
||||
class TestFileTypeMissingInitialization(TestCase):
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Deprecate the :class:`argparse.FileType` type converter.
|
Loading…
Reference in New Issue