Skip to content

Commit

Permalink
bpo-42572: Improve argparse docs for the type parameter. (pythonGH-23849
Browse files Browse the repository at this point in the history
)
  • Loading branch information
rhettinger authored Dec 20, 2020
1 parent a44ce6c commit b0398a4
Showing 1 changed file with 57 additions and 50 deletions.
107 changes: 57 additions & 50 deletions Doc/library/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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'>)

``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.


choices
Expand Down

0 comments on commit b0398a4

Please sign in to comment.