8000 gh-126180: Remove getopt and optparse deprecation notices by ncoghlan · Pull Request #126227 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-126180: Remove getopt and optparse deprecation notices #126227

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Updates based on Serhiy's feedback
  • Loading branch information
ncoghlan committed Nov 2, 2024
commit 93d7161893a84656403365098a2d04f0d1434a36
18 changes: 9 additions & 9 deletions Doc/howto/argparse-optparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,8 @@
Upgrading optparse code
==========================

Originally, the :mod:`argparse` module had attempted to maintain compatibility
with :mod:`optparse`. However, :mod:`optparse` was difficult to extend
transparently, particularly with the changes required to support
``nargs=`` specifiers and better usage messages. When most everything in
:mod:`optparse` had either been copy-pasted over or monkey-patched, it no
longer seemed practical to try to maintain the backwards compatibility.

The :mod:`argparse` module improves on the :mod:`optparse`
module in a number of ways including:
The :mod:`argparse` module offers several higher level features not natively
provided by the :mod:`optparse` module, including:

* Handling positional arguments.
* Supporting subcommands.
Expand All @@ -23,6 +16,13 @@ module in a number of ways including:
* Producing more informative usage messages.
* Providing a much simpler interface for custom ``type`` and ``action``.

Originally, the :mod:`argparse` module attempted to maintain compatibility
with :mod:`optparse`. However, :mod:`optparse` was difficult to extend
transparently, particularly with the changes required to support
``nargs=`` specifiers and better usage messages. When most everything in
:mod:`optparse` had either been copy-pasted over or monkey-patched, it no
longer seemed practical to try to maintain the backwards compatibility.

A partial upgrade path from :mod:`optparse` to :mod:`argparse`:

* Replace all :meth:`optparse.OptionParser.add_option` calls with
Expand Down
14 changes: 9 additions & 5 deletions Doc/howto/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ recommended command-line parsing module in the Python standard library.

.. note::

There are two other modules that fulfill the same task, namely
:mod:`getopt` (an equivalent for ``getopt()`` from the C
language) and the lower level :mod:`optparse` module.
Note also that :mod:`argparse` is based on :mod:`optparse`,
and therefore very similar in terms of usage.
The standard library includes two other libraries directly related
to command-line parameter processing: the lower level :mod:`optparse`
module (which may require more code to configure for a given application,
but also allows an application to request behaviors that ``argparse``
doesn't support), and the very low level :mod:`getopt` (which specifically
serves as an equivalent to ``getopt()`` from the C language).
While neither of those modules is covered directly in this guide, many of
the core concepts in ``argparse`` first originated in ``optparse``, so
some aspects of this tutorial will also be relevant to ``optparse`` users.


Concepts
Expand Down
8 changes: 5 additions & 3 deletions Doc/library/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
lower level :mod:`optparse` module serves as a better foundation for
that use case. ``optparse`` (or one of the third party libraries
based on it) may also be worth considering for applications where
stricter adherence to common Unix and Linux command line interface
conventions around the handling of option parameter values that start
with ``-`` is desired.
``argparse`` doesn't support behaviors that the application requires
(such as entirely disabling support for interspersed options and
positional arguments, or stricter adherence to common Unix and Linux
command line interface conventions related to the handling of option
parameter values that start with ``-``).

--------------

Expand Down
82 changes: 54 additions & 28 deletions Doc/library/getopt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

.. note::

The :mod:`getopt` module is a parser for command line options whose API is
designed to be familiar to users of the C :c:func:`!getopt` function. Users who
are unfamiliar with the C :c:func:`!getopt` function or who would like to write
less code and get better help and error messages should consider using the
:mod:`argparse` module instead.
This module is considered feature complete. A more object-oriented and
extensible alternative to this API is provided in the :mod:`optparse`
module. Further functional enhancements for command line parameter
processing are provided either as third party modules on PyPI,
or else as features in the :mod:`argparse` module.

--------------

Expand All @@ -23,6 +23,12 @@
options similar to those supported by GNU software may be used as well via an
optional third argument.

Users who are unfamiliar with the Unix :c:func:`!getopt` function should consider
using the :mod:`argparse` module instead. Users who are familiar with the Unix
:c:func:`!getopt` function, but would like to get equivalent behavior while
writing less code and getting better help and error messages should consider
using the :mod:`optparse` module.

This module provides two functions and an
exception:

Expand Down Expand Up @@ -150,29 +156,49 @@
import optparse

if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option('-o', '--output')
parser.add_option('-v', dest='verbose', action='store_true')
opts, args = parser.parse_args()
process(args, output=opts.output, verbose=opts.verbose)

An equivalent command line interface for this simple case can also be produced
by using the :mod:`argparse` module::

import argparse

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--output')
parser.add_argument('-v', dest='verbose', action='store_true')
args = parser.parse_args()
# ... do something with args.output ...
# ... do something with args.verbose ..

In more complex cases (such as options which accept values), the behaviour
of the ``argparse`` version may diverge from that of the ``getopt`` and
``optparse`` versions due to the way ``argparse`` handles parameter
values that start with ``-``.
parser = optparse.OptionParser()
parser.add_option('-o', '--output')
parser.add_option('-v', dest='verbose', action='store_true')
opts, args = parser.parse_args()
process(args, output=opts.output, verbose=opts.verbose)

A roughly equivalent command line interface for this case can also be
produced by using the :mod:`argparse` module::

import argparse

Check warning on line 168 in Doc/library/getopt.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

Literal block expected; none found. [docutils]

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--output')
parser.add_argument('-v', dest='verbose', action='store_true')
parser.add_argument('rest', nargs='*')
args = parser.parse_args()
process(args.rest, output=args.output, verbose=args.verbose)

However, unlike the ``optparse`` example, this ``argparse`` example will
handle some parameter combinations differently from the way the ``getopt``
version would handle them. For example (amongst other differences):

* supplying ``-o -v`` gives ``output="-v"`` and ``verbose=False``
for both ``getopt`` and ``optparse``,
but a usage error with ``argparse``
(complaining that no value has been supplied for ``-o/--output``,
since ``-v`` is interpreted as meaning the verbosity flag)
* similarly, supplying ``-o --`` gives ``output="--"`` and ``args=()``
for both ``getopt`` and ``optparse``,
but a usage error with ``argparse``
(also complaining that no value has been supplied for ``-o/--output``,
since ``--`` is interpreted as terminating the option processing
and treating all remaining values as positional arguments)
* supplying ``-o=foo`` gives ``output="=foo"``
for both ``getopt`` and ``optparse``,
but gives ``output="foo"`` with ``argparse``
(since ``=`` is special cased as an alternative separator for
option parameter values)

Whether these differing behaviors in the ``argparse`` version are
considered desirable or a problem will depend on the specific command line
application use case.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serhiy-storchaka ^^^ is the main reason I don't think we should walk back the default argparse recommendation. Many (probably even most) Python devs would consider argparse to be in the right for these 3 examples, and getopt/optparse the ones doing something dubious (even if what they're doing is the common Unix convention).

The discrepancies are valid reasons for reverting the soft deprecation (optparse can genuinely be used to define command line behaviours that argparse doesn't support), but it will take less arguable examples than these ones to make the case that optparse should be preferred over argparse in general.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to write about these peculiarities in the argparse module documentation. The getopt module documentation is not the place where users will search information about the argparse module.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. I like that we're giving a list of differences in behavior, but I think it makes more sense to put this in a common section :ref:'d from all three of our command line parsing module docs. I don't expect anyone reading argparse (most likely) or optparse (likely) docs to find this in the getopt docs (presumed least likely to be read).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved this to a new "Choosing an argument parsing library" section at the start of the optparse docs. getopt still has the example code, but refers to that new section to explain the differences.

Similar, I made the note at the start of the argparse docs shorter, and added a link to this new section.


.. seealso::

Expand Down
30 changes: 18 additions & 12 deletions Doc/library/optparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@

.. note::

:mod:`argparse` (rather than this module) is the recommended standard
library module for implementing command line applications unless one
This module is considered feature complete. Further functional enhancements for
command line parameter processing are provided either as third party modules on
PyPI, or else as features in the :mod:`argparse` module.

.. note::

The higher level :mod:`argparse` (rather than this module) is the recommended
standard library module for implementing command line applications unless one
of the following caveats applies:

* the application requires additional control over the way options and
Expand All @@ -24,24 +30,24 @@
exact way it works in practice is undesirable for some use cases)
* the application requires additional control over the handling of options
which accept parameter values that may start with ``-`` (such as delegated
options to be passed to invoked subprocesses).
options to be passed to invoked subprocesses)
* the application requires some other command line parameter processing
behavior which ``argparse`` does not support, but which can be implemented
in terms of the lower level interface offered by ``optparse``

These ``argparse`` caveats also mean that :mod:`optparse` is likely to
provide a better foundation for library authors *writing* third party
command line argument processing libraries.

.. seealso::

The :pypi:`"click" package <click>` is an ``optparse`` based third party
argument processing library which allows command line applications to be
developed as a set of appropriately decorated command implementation
functions.

.. se 69EF ealso::
:pypi:`click` is a third party argument processing library (originally
based on ``optparse``), which allows command line applications to be
developed as a set of decorated command implementation functions.

The :pypi:`"Typer" package <click>` is a ``click`` based third party
argument processing library which allows the use of annotated Python
type hints to define an application's command line interface.
Other third party libraries, such as :pypi:`typer` or :pypi:`msgspec-click`,
allow command line interfaces to be specified in ways that effectively
integrate with static checking of Python type annotations.

--------------

Expand Down
Loading
0