Commit 3a31ecd8 authored by Benjamin Peterson's avatar Benjamin Peterson

merge heads

parents 6a020e80 74d6c250
...@@ -976,9 +976,9 @@ See the section on the default_ keyword argument for information on when the ...@@ -976,9 +976,9 @@ See the section on the default_ keyword argument for information on when the
``type`` argument is applied to default arguments. ``type`` argument is applied to default arguments.
To ease the use of various types of files, the argparse module provides the To ease the use of various types of files, the argparse module provides the
factory FileType which takes the ``mode=`` and ``bufsize=`` arguments of the factory FileType which takes the ``mode=``, ``bufsize=``, ``encoding=`` and
:func:`open` function. For example, ``FileType('w')`` can be used to create a ``errors=`` arguments of the :func:`open` function. For example,
writable file:: ``FileType('w')`` can be used to create a writable file::
>>> parser = argparse.ArgumentParser() >>> parser = argparse.ArgumentParser()
>>> parser.add_argument('bar', type=argparse.FileType('w')) >>> parser.add_argument('bar', type=argparse.FileType('w'))
...@@ -1617,17 +1617,19 @@ Sub-commands ...@@ -1617,17 +1617,19 @@ Sub-commands
FileType objects FileType objects
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
.. class:: FileType(mode='r', bufsize=None) .. class:: FileType(mode='r', bufsize=-1, encoding=None, errors=None)
The :class:`FileType` factory creates objects that can be passed to the type The :class:`FileType` factory creates objects that can be passed to the type
argument of :meth:`ArgumentParser.add_argument`. Arguments that have argument of :meth:`ArgumentParser.add_argument`. Arguments that have
:class:`FileType` objects as their type will open command-line arguments as files :class:`FileType` objects as their type will open command-line arguments as
with the requested modes and buffer sizes:: files with the requested modes, buffer sizes, encodings and error handling
(see the :func:`open` function for more details)::
>>> parser = argparse.ArgumentParser() >>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--output', type=argparse.FileType('wb', 0)) >>> parser.add_argument('--raw', type=argparse.FileType('wb', 0))
>>> parser.parse_args(['--output', 'out']) >>> parser.add_argument('out', type=argparse.FileType('w', encoding='UTF-8'))
Namespace(output=<_io.BufferedWriter name='out'>) >>> parser.parse_args(['--raw', 'raw.dat', 'file.txt'])
Namespace(out=<_io.TextIOWrapper name='file.txt' mode='w' encoding='UTF-8'>, raw=<_io.FileIO name='raw.dat' mode='wb'>)
FileType objects understand the pseudo-argument ``'-'`` and automatically FileType objects understand the pseudo-argument ``'-'`` and automatically
convert this into ``sys.stdin`` for readable :class:`FileType` objects and convert this into ``sys.stdin`` for readable :class:`FileType` objects and
......
...@@ -1140,11 +1140,17 @@ class FileType(object): ...@@ -1140,11 +1140,17 @@ class FileType(object):
same values as the builtin open() function. same values as the builtin open() function.
- bufsize -- The file's desired buffer size. Accepts the same values as - bufsize -- The file's desired buffer size. Accepts the same values as
the builtin open() function. the builtin open() function.
- encoding -- The file's encoding. Accepts the same values as the
the builtin open() function.
- errors -- A string indicating how encoding and decoding errors are to
be handled. Accepts the same value as the builtin open() function.
""" """
def __init__(self, mode='r', bufsize=-1): def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None):
self._mode = mode self._mode = mode
self._bufsize = bufsize self._bufsize = bufsize
self._encoding = encoding
self._errors = errors
def __call__(self, string): def __call__(self, string):
# the special argument "-" means sys.std{in,out} # the special argument "-" means sys.std{in,out}
...@@ -1159,14 +1165,18 @@ class FileType(object): ...@@ -1159,14 +1165,18 @@ class FileType(object):
# all other arguments are used as file names # all other arguments are used as file names
try: try:
return open(string, self._mode, self._bufsize) return open(string, self._mode, self._bufsize, self._encoding,
self._errors)
except IOError as e: except IOError as e:
message = _("can't open '%s': %s") message = _("can't open '%s': %s")
raise ArgumentTypeError(message % (string, e)) raise ArgumentTypeError(message % (string, e))
def __repr__(self): def __repr__(self):
args = self._mode, self._bufsize args = self._mode, self._bufsize
args_str = ', '.join(repr(arg) for arg in args if arg != -1) kwargs = [('encoding', self._encoding), ('errors', self._errors)]
args_str = ', '.join([repr(arg) for arg in args if arg != -1] +
['%s=%r' % (kw, arg) for kw, arg in kwargs
if arg is not None])
return '%s(%s)' % (type(self).__name__, args_str) return '%s(%s)' % (type(self).__name__, args_str)
# =========================== # ===========================
......
...@@ -14,6 +14,7 @@ import argparse ...@@ -14,6 +14,7 @@ import argparse
from io import StringIO from io import StringIO
from test import support from test import support
from unittest import mock
class StdIOBuffer(StringIO): class StdIOBuffer(StringIO):
pass pass
...@@ -1421,6 +1422,19 @@ class TestFileTypeRepr(TestCase): ...@@ -1421,6 +1422,19 @@ class TestFileTypeRepr(TestCase):
type = argparse.FileType('wb', 1) type = argparse.FileType('wb', 1)
self.assertEqual("FileType('wb', 1)", repr(type)) self.assertEqual("FileType('wb', 1)", repr(type))
def test_r_latin(self):
type = argparse.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')
self.assertEqual("FileType('w', encoding='big5', errors='ignore')",
repr(type))
def test_r_1_replace(self):
type = argparse.FileType('r', 1, errors='replace')
self.assertEqual("FileType('r', 1, errors='replace')", repr(type))
class RFile(object): class RFile(object):
seen = {} seen = {}
...@@ -1557,6 +1571,24 @@ class TestFileTypeWB(TempDirMixin, ParserTestCase): ...@@ -1557,6 +1571,24 @@ class TestFileTypeWB(TempDirMixin, ParserTestCase):
] ]
class TestFileTypeOpenArgs(TestCase):
"""Test that open (the builtin) is correctly called"""
def test_open_args(self):
FT = argparse.FileType
cases = [
(FT('rb'), ('rb', -1, None, None)),
(FT('w', 1), ('w', 1, None, None)),
(FT('w', errors='replace'), ('w', -1, None, 'replace')),
(FT('wb', encoding='big5'), ('wb', -1, 'big5', None)),
(FT('w', 0, 'l1', 'strict'), ('w', 0, 'l1', 'strict')),
]
with mock.patch('builtins.open') as m:
for type, args in cases:
type('foo')
m.assert_called_with('foo', *args)
class TestTypeCallable(ParserTestCase): class TestTypeCallable(ParserTestCase):
"""Test some callables as option/argument types""" """Test some callables as option/argument types"""
......
...@@ -768,6 +768,7 @@ Laura Matson ...@@ -768,6 +768,7 @@ Laura Matson
Graham Matthews Graham Matthews
Dieter Maurer Dieter Maurer
Daniel May Daniel May
Lucas Maystre
Arnaud Mazin Arnaud Mazin
Rebecca McCreary Rebecca McCreary
Kirk McDonald Kirk McDonald
......
...@@ -167,6 +167,9 @@ Core and Builtins ...@@ -167,6 +167,9 @@ Core and Builtins
Library Library
------- -------
- Issue #11175: argparse.FileType now accepts encoding and errors
arguments. Patch by Lucas Maystre.
- Issue #16488: epoll() objects now support the `with` statement. Patch - Issue #16488: epoll() objects now support the `with` statement. Patch
by Serhiy Storchaka. by Serhiy Storchaka.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment