[Python-checkins] bpo-42572: Improve argparse docs for the type parameter. (GH-23849)

rhettinger webhook-mailer at python.org
Sun Dec 20 13:15:03 EST 2020

commit: b0398a4b7fb5743f6dbb72ac6b2926e0a0c11498
branch: master
author: Raymond Hettinger <rhettinger at users.noreply.github.com>
committer: rhettinger <rhettinger at users.noreply.github.com>
date: 2020-12-20T10:14:54-08:00

bpo-42572:  Improve argparse docs for the type parameter. (GH-23849)

M Doc/library/argparse.rst

diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 02cd70f4f71cd..1a298cdd2b534 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -1050,63 +1050,70 @@ command-line argument was not present::
-By default, :class:`ArgumentParser` objects read command-line arguments in as simple
+By default, the parser reads command-line arguments in as simple
 strings. However, quite often the command-line string should instead be
-interpreted as another type, like a :class:`float` or :class:`int`.  The
-``type`` keyword argument of :meth:`~ArgumentParser.add_argument` allows any
-necessary type-checking and type conversions to be performed.  Common built-in
-types and functions can be used directly as the value of the ``type`` argument::
+interpreted as another type, such as a :class:`float` or :class:`int`.  The
+``type`` keyword for :meth:`~ArgumentParser.add_argument` allows any
+necessary type-checking and type conversions to be performed.
-   >>> parser = argparse.ArgumentParser()
-   >>> parser.add_argument('foo', type=int)
-   >>> parser.add_argument('bar', type=open)
-   >>> parser.parse_args('2 temp.txt'.split())
-   Namespace(bar=<_io.TextIOWrapper name='temp.txt' encoding='UTF-8'>, foo=2)
+If the type_ keyword is used with the default_ keyword, the type converter
+is only applied if the default is a string.
-See the section on the default_ keyword argument for information on when the
-``type`` argument is applied to default arguments.
+The argument to ``type`` can be any callable that accepts a single string.
+If the function raises :exc:`ArgumentTypeError`, :exc:`TypeError`, or
+:exc:`ValueError`, the exception is caught and a nicely formatted error
+message is displayed.  No other exception types are handled.
-To ease the use of various types of files, the argparse module provides the
-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::
+Common built-in types and functions can be used as type converters:
-   >>> parser = argparse.ArgumentParser()
-   >>> parser.add_argument('bar', type=argparse.FileType('w'))
-   >>> parser.parse_args(['out.txt'])
-   Namespace(bar=<_io.TextIOWrapper name='out.txt' encoding='UTF-8'>)
-``type=`` can take any callable that takes a single string argument and returns
-the converted value::
-   >>> def perfect_square(string):
-   ...     value = int(string)
-   ...     sqrt = math.sqrt(value)
-   ...     if sqrt != int(sqrt):
-   ...         msg = "%r is not a perfect square" % string
-   ...         raise argparse.ArgumentTypeError(msg)
-   ...     return value
-   ...
-   >>> parser = argparse.ArgumentParser(prog='PROG')
-   >>> parser.add_argument('foo', type=perfect_square)
-   >>> parser.parse_args(['9'])
-   Namespace(foo=9)
-   >>> parser.parse_args(['7'])
-   usage: PROG [-h] foo
-   PROG: error: argument foo: '7' is not a perfect square
+.. testcode::
-The choices_ keyword argument may be more convenient for type checkers that
-simply check against a range of values::
+   import argparse
+   import pathlib
-   >>> parser = argparse.ArgumentParser(prog='PROG')
-   >>> parser.add_argument('foo', type=int, choices=range(5, 10))
-   >>> parser.parse_args(['7'])
-   Namespace(foo=7)
-   >>> parser.parse_args(['11'])
-   usage: PROG [-h] {5,6,7,8,9}
-   PROG: error: argument foo: invalid choice: 11 (choose from 5, 6, 7, 8, 9)
-See the choices_ section for more details.
+   parser = argparse.ArgumentParser()
+   parser.add_argument('count', type=int)
+   parser.add_argument('distance', type=float)
+   parser.add_argument('street', type=ascii)
+   parser.add_argument('code_point', type=ord)
+   parser.add_argument('source_file', type=open)
+   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:
+.. doctest::
+   >>> def hyphenated(string):
+   ...     return '-'.join([word[:4] for word in string.casefold().split()])
+   ...
+   >>> parser = argparse.ArgumentParser()
+   >>> _ = parser.add_argument('short_title', type=hyphenated)
+   >>> parser.parse_args(['"The Tale of Two Cities"'])
+   Namespace(short_title='"the-tale-of-two-citi')
+The :func:`bool` function is not recommended as a type converter.  All it does
+is convert empty strings to ``False`` and non-empty strings to ``True``.
+This is usually not what is desired.
+In general, the ``type`` keyword is a convenience that should only be used for
+simple conversions that can only raise one of the three supported exceptions.
+Anything with more interesting error-handling or resource management should be
+done downstream after the arguments are parsed.
+For example, JSON or YAML conversions have complex error cases that require
+better reporting than can be given by the ``type`` keyword.  An
+:exc:`~json.JSONDecodeError` would not be well formatted and a
+:exc:`FileNotFound` exception would not be handled at all.
+Even :class:`~argparse.FileType` has its limitations for use with the ``type``
+keyword.  If one argument uses *FileType* and then a subsequent argument fails,
+an error is reported but the file is not automatically closed.  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.
+For type checkers that simply check against a fixed set of values, consider
+using the choices_ keyword instead.

More information about the Python-checkins mailing list