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

This commit is contained in:
Raymond Hettinger 2020-12-20 10:14:54 -08:00 committed by GitHub
parent a44ce6c9f7
commit b0398a4b7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 53 additions and 46 deletions

View File

@ -1050,63 +1050,70 @@ command-line argument was not present::
type
^^^^
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'>)
.. testcode::
``type=`` can take any callable that takes a single string argument and returns
the converted value::
import argparse
import pathlib
>>> 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()
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(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
>>> 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 choices_ keyword argument may be more convenient for type checkers that
simply check against a range of values::
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.
>>> 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)
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.
See the choices_ section for more details.
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.
choices