diff -r ae047ebe10f9 Doc/library/argparse.rst --- a/Doc/library/argparse.rst Wed Dec 11 17:21:13 2013 -0600 +++ b/Doc/library/argparse.rst Thu Dec 12 05:54:06 2013 -0800 @@ -1648,19 +1648,21 @@ FileType objects ^^^^^^^^^^^^^^^^ -.. class:: FileType(mode='r', bufsize=-1, encoding=None, errors=None) +.. class:: FileType(mode='r', bufsize=-1, encoding=None, errors=None, expanduser=False) 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, buffer sizes, encodings and error handling + files with the requested modes, buffer sizes, encodings and error handling. + If expanduser is true then the filename will be expanded by :meth:`os.path.expanduser`. (see the :func:`open` function for more details):: >>> parser = argparse.ArgumentParser() >>> 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'>) + >>> parser.add_argument('in', type=argparse.FileType('r', expanduser=True)) + >>> parser.parse_args(['--raw', 'raw.dat', 'file.txt', '~/in']) + Namespace(out=<_io.TextIOWrapper name='file.txt' mode='w' encoding='UTF-8'>, raw=<_io.FileIO name='raw.dat' mode='wb'>, in=<_io.TextIOWrapper name='/home/username/test' mode='r' encoding='UTF-8'>) FileType objects understand the pseudo-argument ``'-'`` and automatically convert this into ``sys.stdin`` for readable :class:`FileType` objects and diff -r ae047ebe10f9 Lib/argparse.py --- a/Lib/argparse.py Wed Dec 11 17:21:13 2013 -0600 +++ b/Lib/argparse.py Thu Dec 12 05:54:06 2013 -0800 @@ -1145,13 +1145,16 @@ 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. + - expanduser -- A boolean specifying if os.path.expanduser should be called on the path + before opening """ - def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None): + def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None, expanduser=False): self._mode = mode self._bufsize = bufsize self._encoding = encoding self._errors = errors + self._expanduser = expanduser def __call__(self, string): # the special argument "-" means sys.std{in,out} @@ -1163,6 +1166,9 @@ else: msg = _('argument "-" with mode %r') % self._mode raise ValueError(msg) + else: + if self._expanduser: + string = _os.path.expanduser(string) # all other arguments are used as file names try: diff -r ae047ebe10f9 Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py Wed Dec 11 17:21:13 2013 -0600 +++ b/Lib/test/test_argparse.py Thu Dec 12 05:54:06 2013 -0800 @@ -1588,6 +1588,39 @@ type('foo') m.assert_called_with('foo', *args) +class TestFileTypeExpandUser(TestCase): + """ Test that tilde is expanded correctly """ + + def test_open_tilde(self): + FT = argparse.FileType + foo = '~/fo~o' + expandedfoo = os.path.expanduser(foo) + cases = [ + # Expand + (FT('rb', expanduser=True), expandedfoo, ('rb', -1, None, None)), + (FT('w', 1, expanduser=True), expandedfoo, ('w', 1, None, None)), + (FT('w', errors='replace', expanduser=True), expandedfoo,('w', -1, None, 'replace')), + (FT('wb', encoding='big5', expanduser=True), expandedfoo, ('wb', -1, 'big5', None)), + (FT('w', 0, 'l1', 'strict', expanduser=True), expandedfoo, ('w', 0, 'l1', 'strict')), + # Don't expand + (FT('rb', expanduser=False), foo, ('rb', -1, None, None)), + (FT('w', 1, expanduser=False), foo, ('w', 1, None, None)), + (FT('w', errors='replace', expanduser=False), foo,('w', -1, None, 'replace')), + (FT('wb', encoding='big5', expanduser=False), foo, ('wb', -1, 'big5', None)), + (FT('w', 0, 'l1', 'strict', expanduser=False), foo, ('w', 0, 'l1', 'strict')), + # default + (FT('rb'), foo, ('rb', -1, None, None)), + (FT('w', 1), foo, ('w', 1, None, None)), + (FT('w', errors='replace'), foo, ('w', -1, None, 'replace')), + (FT('wb', encoding='big5'), foo, ('wb', -1, 'big5', None)), + (FT('w', 0, 'l1', 'strict'), foo, ('w', 0, 'l1', 'strict')), + ] + with mock.patch('builtins.open') as m: + for type, targetfilename, args in cases: + type(foo) + m.assert_called_with(targetfilename, *args) + + class TestTypeCallable(ParserTestCase): """Test some callables as option/argument types"""