(original) (raw)

changeset: 80864:d5a0698a8354 user: Petri Lehtinen petri@digip.org date: Sat Dec 15 22:39:32 2012 +0200 files: Doc/library/argparse.rst Lib/argparse.py Lib/test/test_argparse.py Misc/ACKS Misc/NEWS description: #11175: argparse.FileType now accepts encoding and errors arguments. Patch by Lucas Maystre. diff -r d6f61cd364d9 -r d5a0698a8354 Doc/library/argparse.rst --- a/Doc/library/argparse.rst Sat Dec 15 21:14:21 2012 +0100 +++ b/Doc/library/argparse.rst Sat Dec 15 22:39:32 2012 +0200 @@ -976,9 +976,9 @@ ``type`` argument is applied to default arguments. 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 -:func:`open` function. For example, ``FileType('w')`` can be used to create a -writable file:: +factory FileType which takes the ``mode=``, ``bufsize=``, ``encoding=`` and +``errors=`` arguments of the :func:`open` function. For example, +``FileType('w')`` can be used to create a writable file:: >>> parser = argparse.ArgumentParser() >>> parser.add_argument('bar', type=argparse.FileType('w')) @@ -1617,17 +1617,19 @@ 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 argument of :meth:`ArgumentParser.add_argument`. Arguments that have - :class:`FileType` objects as their type will open command-line arguments as files - with the requested modes and buffer sizes:: + :class:`FileType` objects as their type will open command-line arguments as + files with the requested modes, buffer sizes, encodings and error handling + (see the :func:`open` function for more details):: >>> parser = argparse.ArgumentParser() - >>> parser.add_argument('--output', type=argparse.FileType('wb', 0)) - >>> parser.parse_args(['--output', 'out']) - Namespace(output=<_io.BufferedWriter name='out'>) + >>> parser.add_argument('--raw', type=argparse.FileType('wb', 0)) + >>> parser.add_argument('out', type=argparse.FileType('w', encoding='UTF-8')) + >>> 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 convert this into ``sys.stdin`` for readable :class:`FileType` objects and diff -r d6f61cd364d9 -r d5a0698a8354 Lib/argparse.py --- a/Lib/argparse.py Sat Dec 15 21:14:21 2012 +0100 +++ b/Lib/argparse.py Sat Dec 15 22:39:32 2012 +0200 @@ -1140,11 +1140,17 @@ same values as the builtin open() function. - bufsize -- The file's desired buffer size. Accepts the same values as 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._bufsize = bufsize + self._encoding = encoding + self._errors = errors def __call__(self, string): # the special argument "-" means sys.std{in,out} @@ -1159,14 +1165,18 @@ # all other arguments are used as file names try: - return open(string, self._mode, self._bufsize) + return open(string, self._mode, self._bufsize, self._encoding, + self._errors) except IOError as e: message = _("can't open '%s': %s") raise ArgumentTypeError(message % (string, e)) def __repr__(self): 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) # =========================== diff -r d6f61cd364d9 -r d5a0698a8354 Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py Sat Dec 15 21:14:21 2012 +0100 +++ b/Lib/test/test_argparse.py Sat Dec 15 22:39:32 2012 +0200 @@ -14,6 +14,7 @@ from io import StringIO from test import support +from unittest import mock class StdIOBuffer(StringIO): pass @@ -1421,6 +1422,19 @@ type = argparse.FileType('wb', 1) 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): seen = {} @@ -1557,6 +1571,24 @@ ] +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): """Test some callables as option/argument types""" diff -r d6f61cd364d9 -r d5a0698a8354 Misc/ACKS --- a/Misc/ACKS Sat Dec 15 21:14:21 2012 +0100 +++ b/Misc/ACKS Sat Dec 15 22:39:32 2012 +0200 @@ -768,6 +768,7 @@ Graham Matthews Dieter Maurer Daniel May +Lucas Maystre Arnaud Mazin Rebecca McCreary Kirk McDonald diff -r d6f61cd364d9 -r d5a0698a8354 Misc/NEWS --- a/Misc/NEWS Sat Dec 15 21:14:21 2012 +0100 +++ b/Misc/NEWS Sat Dec 15 22:39:32 2012 +0200 @@ -167,6 +167,9 @@ 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 by Serhiy Storchaka. /petri@digip.org