Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 74d6c25

Browse files
committed
#11175: argparse.FileType now accepts encoding and errors arguments.
Patch by Lucas Maystre.
1 parent 09bb89b commit 74d6c25

5 files changed

Lines changed: 60 additions & 12 deletions

File tree

Doc/library/argparse.rst

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -976,9 +976,9 @@ See the section on the default_ keyword argument for information on when the
976976
``type`` argument is applied to default arguments.
977977

978978
To ease the use of various types of files, the argparse module provides the
979-
factory FileType which takes the ``mode=`` and ``bufsize=`` arguments of the
980-
:func:`open` function. For example, ``FileType('w')`` can be used to create a
981-
writable file::
979+
factory FileType which takes the ``mode=``, ``bufsize=``, ``encoding=`` and
980+
``errors=`` arguments of the :func:`open` function. For example,
981+
``FileType('w')`` can be used to create a writable file::
982982

983983
>>> parser = argparse.ArgumentParser()
984984
>>> parser.add_argument('bar', type=argparse.FileType('w'))
@@ -1617,17 +1617,19 @@ Sub-commands
16171617
FileType objects
16181618
^^^^^^^^^^^^^^^^
16191619

1620-
.. class:: FileType(mode='r', bufsize=None)
1620+
.. class:: FileType(mode='r', bufsize=-1, encoding=None, errors=None)
16211621

16221622
The :class:`FileType` factory creates objects that can be passed to the type
16231623
argument of :meth:`ArgumentParser.add_argument`. Arguments that have
1624-
:class:`FileType` objects as their type will open command-line arguments as files
1625-
with the requested modes and buffer sizes::
1624+
:class:`FileType` objects as their type will open command-line arguments as
1625+
files with the requested modes, buffer sizes, encodings and error handling
1626+
(see the :func:`open` function for more details)::
16261627

16271628
>>> parser = argparse.ArgumentParser()
1628-
>>> parser.add_argument('--output', type=argparse.FileType('wb', 0))
1629-
>>> parser.parse_args(['--output', 'out'])
1630-
Namespace(output=<_io.BufferedWriter name='out'>)
1629+
>>> parser.add_argument('--raw', type=argparse.FileType('wb', 0))
1630+
>>> parser.add_argument('out', type=argparse.FileType('w', encoding='UTF-8'))
1631+
>>> parser.parse_args(['--raw', 'raw.dat', 'file.txt'])
1632+
Namespace(out=<_io.TextIOWrapper name='file.txt' mode='w' encoding='UTF-8'>, raw=<_io.FileIO name='raw.dat' mode='wb'>)
16311633

16321634
FileType objects understand the pseudo-argument ``'-'`` and automatically
16331635
convert this into ``sys.stdin`` for readable :class:`FileType` objects and

Lib/argparse.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,11 +1140,17 @@ class FileType(object):
11401140
same values as the builtin open() function.
11411141
- bufsize -- The file's desired buffer size. Accepts the same values as
11421142
the builtin open() function.
1143+
- encoding -- The file's encoding. Accepts the same values as the
1144+
the builtin open() function.
1145+
- errors -- A string indicating how encoding and decoding errors are to
1146+
be handled. Accepts the same value as the builtin open() function.
11431147
"""
11441148

1145-
def __init__(self, mode='r', bufsize=-1):
1149+
def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None):
11461150
self._mode = mode
11471151
self._bufsize = bufsize
1152+
self._encoding = encoding
1153+
self._errors = errors
11481154

11491155
def __call__(self, string):
11501156
# the special argument "-" means sys.std{in,out}
@@ -1159,14 +1165,18 @@ def __call__(self, string):
11591165

11601166
# all other arguments are used as file names
11611167
try:
1162-
return open(string, self._mode, self._bufsize)
1168+
return open(string, self._mode, self._bufsize, self._encoding,
1169+
self._errors)
11631170
except IOError as e:
11641171
message = _("can't open '%s': %s")
11651172
raise ArgumentTypeError(message % (string, e))
11661173

11671174
def __repr__(self):
11681175
args = self._mode, self._bufsize
1169-
args_str = ', '.join(repr(arg) for arg in args if arg != -1)
1176+
kwargs = [('encoding', self._encoding), ('errors', self._errors)]
1177+
args_str = ', '.join([repr(arg) for arg in args if arg != -1] +
1178+
['%s=%r' % (kw, arg) for kw, arg in kwargs
1179+
if arg is not None])
11701180
return '%s(%s)' % (type(self).__name__, args_str)
11711181

11721182
# ===========================

Lib/test/test_argparse.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from io import StringIO
1515

1616
from test import support
17+
from unittest import mock
1718
class StdIOBuffer(StringIO):
1819
pass
1920

@@ -1421,6 +1422,19 @@ def test_wb_1(self):
14211422
type = argparse.FileType('wb', 1)
14221423
self.assertEqual("FileType('wb', 1)", repr(type))
14231424

1425+
def test_r_latin(self):
1426+
type = argparse.FileType('r', encoding='latin_1')
1427+
self.assertEqual("FileType('r', encoding='latin_1')", repr(type))
1428+
1429+
def test_w_big5_ignore(self):
1430+
type = argparse.FileType('w', encoding='big5', errors='ignore')
1431+
self.assertEqual("FileType('w', encoding='big5', errors='ignore')",
1432+
repr(type))
1433+
1434+
def test_r_1_replace(self):
1435+
type = argparse.FileType('r', 1, errors='replace')
1436+
self.assertEqual("FileType('r', 1, errors='replace')", repr(type))
1437+
14241438

14251439
class RFile(object):
14261440
seen = {}
@@ -1557,6 +1571,24 @@ class TestFileTypeWB(TempDirMixin, ParserTestCase):
15571571
]
15581572

15591573

1574+
class TestFileTypeOpenArgs(TestCase):
1575+
"""Test that open (the builtin) is correctly called"""
1576+
1577+
def test_open_args(self):
1578+
FT = argparse.FileType
1579+
cases = [
1580+
(FT('rb'), ('rb', -1, None, None)),
1581+
(FT('w', 1), ('w', 1, None, None)),
1582+
(FT('w', errors='replace'), ('w', -1, None, 'replace')),
1583+
(FT('wb', encoding='big5'), ('wb', -1, 'big5', None)),
1584+
(FT('w', 0, 'l1', 'strict'), ('w', 0, 'l1', 'strict')),
1585+
]
1586+
with mock.patch('builtins.open') as m:
1587+
for type, args in cases:
1588+
type('foo')
1589+
m.assert_called_with('foo', *args)
1590+
1591+
15601592
class TestTypeCallable(ParserTestCase):
15611593
"""Test some callables as option/argument types"""
15621594

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@ Laura Matson
768768
Graham Matthews
769769
Dieter Maurer
770770
Daniel May
771+
Lucas Maystre
771772
Arnaud Mazin
772773
Rebecca McCreary
773774
Kirk McDonald

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ Core and Builtins
167167
Library
168168
-------
169169

170+
- Issue #11175: argparse.FileType now accepts encoding and errors
171+
arguments. Patch by Lucas Maystre.
172+
170173
- Issue #16488: epoll() objects now support the `with` statement. Patch
171174
by Serhiy Storchaka.
172175

0 commit comments

Comments
 (0)