summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Sottile <asottile@umich.edu>2019-08-10 18:28:32 -0700
committerAnthony Sottile <asottile@umich.edu>2019-08-17 20:09:45 -0700
commitb66ebd7034090f96cb0806e5e2d8026b6e35d045 (patch)
treea354bc5e4b62fd2949e16c1d404b9ccc5ec1c041
parent03cb85f556b61d408a578becab50936ba2c246e7 (diff)
downloadflake8-b66ebd7034090f96cb0806e5e2d8026b6e35d045.tar.gz
move from optparse to argparse
-rw-r--r--.coveragerc22
-rw-r--r--.pylintrc4
-rw-r--r--docs/source/internal/option_handling.rst4
-rw-r--r--docs/source/plugin-development/plugin-parameters.rst22
-rw-r--r--src/flake8/api/legacy.py5
-rw-r--r--src/flake8/checker.py2
-rw-r--r--src/flake8/formatting/base.py6
-rw-r--r--src/flake8/main/application.py12
-rw-r--r--src/flake8/main/debug.py47
-rw-r--r--src/flake8/main/options.py37
-rw-r--r--src/flake8/main/vcs.py37
-rw-r--r--src/flake8/options/aggregator.py4
-rw-r--r--src/flake8/options/config.py8
-rw-r--r--src/flake8/options/manager.py296
-rw-r--r--src/flake8/plugins/pyflakes.py2
-rw-r--r--src/flake8/style_guide.py2
-rw-r--r--tests/integration/test_main.py36
-rw-r--r--tests/unit/conftest.py4
-rw-r--r--tests/unit/test_application.py6
-rw-r--r--tests/unit/test_base_formatter.py6
-rw-r--r--tests/unit/test_debug.py14
-rw-r--r--tests/unit/test_decision_engine.py6
-rw-r--r--tests/unit/test_filenameonly_formatter.py6
-rw-r--r--tests/unit/test_merged_config_parser.py2
-rw-r--r--tests/unit/test_nothing_formatter.py6
-rw-r--r--tests/unit/test_option.py55
-rw-r--r--tests/unit/test_option_manager.py103
-rw-r--r--tests/unit/test_plugin.py4
-rw-r--r--tests/unit/test_style_guide.py6
-rw-r--r--tox.ini4
30 files changed, 464 insertions, 304 deletions
diff --git a/.coveragerc b/.coveragerc
index 0a3db04..df0a929 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,11 +1,31 @@
[run]
+parallel = True
branch = True
source =
flake8
-
+omit =
+ # Don't complain if non-runnable code isn't run
+ */__main__.py
[paths]
source =
src/flake8
.tox/*/lib/python*/site-packages/flake8
.tox/pypy/site-packages/flake8
+
+[report]
+show_missing = True
+skip_covered = True
+exclude_lines =
+ # Have to re-enable the standard pragma
+ \#\s*pragma: no cover
+
+ # Don't complain if tests don't hit defensive assertion code:
+ ^\s*raise AssertionError\b
+ ^\s*raise NotImplementedError\b
+ ^\s*return NotImplemented\b
+ ^\s*raise$
+
+ # Don't complain if non-runnable code isn't run:
+ ^if __name__ == ['"]__main__['"]:$
+ ^\s*if False:
diff --git a/.pylintrc b/.pylintrc
index d6e52a8..a23de97 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -354,10 +354,6 @@ max-bool-expr=5
[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=optparse
-
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
diff --git a/docs/source/internal/option_handling.rst b/docs/source/internal/option_handling.rst
index 86b7021..dd76706 100644
--- a/docs/source/internal/option_handling.rst
+++ b/docs/source/internal/option_handling.rst
@@ -28,7 +28,7 @@ undocumented attribute that pep8 looks for, |Flake8| 3 now accepts a parameter
to ``add_option``, specifically ``parse_from_config`` which is a boolean
value.
-|Flake8| does this by creating its own abstractions on top of :mod:`optparse`.
+|Flake8| does this by creating its own abstractions on top of :mod:`argparse`.
The first abstraction is the :class:`flake8.options.manager.Option` class. The
second is the :class:`flake8.options.manager.OptionManager`. In fact, we add
three new parameters:
@@ -219,7 +219,7 @@ API Documentation
.. autofunction:: flake8.options.aggregator.aggregate_options
.. autoclass:: flake8.options.manager.Option
- :members: __init__, normalize, to_optparse
+ :members: __init__, normalize, to_argparse
.. autoclass:: flake8.options.manager.OptionManager
:members:
diff --git a/docs/source/plugin-development/plugin-parameters.rst b/docs/source/plugin-development/plugin-parameters.rst
index b7a4221..6dae857 100644
--- a/docs/source/plugin-development/plugin-parameters.rst
+++ b/docs/source/plugin-development/plugin-parameters.rst
@@ -72,7 +72,7 @@ Any plugin that has callable attributes ``add_options`` and
Your ``add_options`` function should expect to receive an instance of
|OptionManager|. An |OptionManager| instance behaves very similarly to
:class:`optparse.OptionParser`. It, however, uses the layer that |Flake8| has
-developed on top of :mod:`optparse` to also handle configuration file parsing.
+developed on top of :mod:`argparse` to also handle configuration file parsing.
:meth:`~flake8.options.manager.OptionManager.add_option` creates an |Option|
which accepts the same parameters as :mod:`optparse` as well as three extra
boolean parameters:
@@ -115,13 +115,13 @@ couple examples from |Flake8|. In each example, we will have
'--max-line-length', type='int', metavar='n',
default=defaults.MAX_LINE_LENGTH, parse_from_config=True,
help='Maximum allowed line length for the entirety of this run. '
- '(Default: %default)',
+ '(Default: %(default)s)',
)
Here we are adding the ``--max-line-length`` command-line option which is
always an integer and will be parsed from the configuration file. Since we
-provide a default, we take advantage of :mod:`optparse`\ 's willingness to
-display that in the help text with ``%default``.
+provide a default, we take advantage of :mod:`argparse`\ 's willingness to
+display that in the help text with ``%(default)s``.
.. code-block:: python
@@ -129,7 +129,7 @@ display that in the help text with ``%default``.
'--select', metavar='errors', default='',
parse_from_config=True, comma_separated_list=True,
help='Comma-separated list of errors and warnings to enable.'
- ' For example, ``--select=E4,E51,W234``. (Default: %default)',
+ ' For example, ``--select=E4,E51,W234``. (Default: %(default)s)',
)
In adding the ``--select`` command-line option, we're also indicating to the
@@ -143,7 +143,7 @@ as a comma-separated list.
comma_separated_list=True, parse_from_config=True,
normalize_paths=True,
help='Comma-separated list of files or directories to exclude.'
- '(Default: %default)',
+ '(Default: %(default)s)',
)
Finally, we show an option that uses all three extra flags. Values from
@@ -152,7 +152,7 @@ list, and then each item will be normalized.
For information about other parameters to
:meth:`~flake8.options.manager.OptionManager.add_option` refer to the
-documentation of :mod:`optparse`.
+documentation of :mod:`argparse`.
Accessing Parsed Options
@@ -160,10 +160,10 @@ Accessing Parsed Options
When a plugin has a callable ``parse_options`` attribute, |Flake8| will call
it and attempt to provide the |OptionManager| instance, the parsed options
-which will be an instance of :class:`optparse.Values`, and the extra arguments
-that were not parsed by the |OptionManager|. If that fails, we will just pass
-the :class:`optparse.Values`. In other words, your ``parse_options``
-callable will have one of the following signatures:
+which will be an instance of :class:`argparse.Namespace`, and the extra
+arguments that were not parsed by the |OptionManager|. If that fails, we will
+just pass the :class:`argparse.Namespace`. In other words, your
+``parse_options`` callable will have one of the following signatures:
.. code-block:: python
diff --git a/src/flake8/api/legacy.py b/src/flake8/api/legacy.py
index fd06a72..16a33f4 100644
--- a/src/flake8/api/legacy.py
+++ b/src/flake8/api/legacy.py
@@ -3,6 +3,7 @@
Previously, users would import :func:`get_style_guide` from ``flake8.engine``.
In 3.0 we no longer have an "engine" module but we maintain the API from it.
"""
+import argparse
import logging
import os.path
@@ -72,10 +73,10 @@ class StyleGuide(object):
self._file_checker_manager = application.file_checker_manager
@property
- def options(self):
+ def options(self): # type: () -> argparse.Namespace
"""Return application's options.
- An instance of :class:`optparse.Values` containing parsed options.
+ An instance of :class:`argparse.Namespace` containing parsed options.
"""
return self._application.options
diff --git a/src/flake8/checker.py b/src/flake8/checker.py
index 6170d88..a08f39c 100644
--- a/src/flake8/checker.py
+++ b/src/flake8/checker.py
@@ -367,7 +367,7 @@ class FileChecker(object):
:param options:
Parsed option values from config and command-line.
:type options:
- optparse.Values
+ argparse.Namespace
"""
self.options = options
self.filename = filename
diff --git a/src/flake8/formatting/base.py b/src/flake8/formatting/base.py
index 520dcbc..36dc905 100644
--- a/src/flake8/formatting/base.py
+++ b/src/flake8/formatting/base.py
@@ -1,7 +1,7 @@
"""The base class and interface for all formatting plugins."""
from __future__ import print_function
-import optparse
+import argparse
from typing import IO, List, Optional, Tuple
if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
@@ -32,13 +32,13 @@ class BaseFormatter(object):
"""
def __init__(self, options):
- # type: (optparse.Values) -> None
+ # type: (argparse.Namespace) -> None
"""Initialize with the options parsed from config and cli.
This also calls a hook, :meth:`after_init`, so subclasses do not need
to call super to call this method.
- :param optparse.Values options:
+ :param argparse.Namespace options:
User specified configuration parsed from both configuration files
and the command-line interface.
"""
diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py
index 4bbee28..6e5373f 100644
--- a/src/flake8/main/application.py
+++ b/src/flake8/main/application.py
@@ -1,8 +1,8 @@
"""Module containing the application logic for Flake8."""
from __future__ import print_function
+import argparse
import logging
-import optparse
import sys
import time
from typing import Dict, List, Optional, Set
@@ -52,8 +52,8 @@ class Application(object):
)
options.register_default_options(self.option_manager)
#: The preliminary options parsed from CLI before plugins are loaded,
- #: into a :class:`optparse.Values` instance
- self.prelim_opts = None # type: optparse.Values
+ #: into a :class:`argparse.Namespace` instance
+ self.prelim_opts = None # type: argparse.Namespace
#: The preliminary arguments parsed from CLI before plugins are loaded
self.prelim_args = None # type: List[str]
#: The instance of :class:`flake8.options.config.ConfigFileFinder`
@@ -77,8 +77,8 @@ class Application(object):
self.file_checker_manager = None # type: checker.Manager
#: The user-supplied options parsed into an instance of
- #: :class:`optparse.Values`
- self.options = None # type: optparse.Values
+ #: :class:`argparse.Namespace`
+ self.options = None # type: argparse.Namespace
#: The left over arguments that were not parsed by
#: :attr:`option_manager`
self.args = None # type: List[str]
@@ -117,7 +117,7 @@ class Application(object):
# printing the version until we aggregate options from config files
# and the command-line. First, let's clone our arguments on the CLI,
# then we'll attempt to remove ``--version`` so that we can avoid
- # triggering the "version" action in optparse. If it's not there, we
+ # triggering the "version" action in argparse. If it's not there, we
# do not need to worry and we can continue. If it is, we successfully
# defer printing the version until just a little bit later.
# Similarly we have to defer printing the help text until later.
diff --git a/src/flake8/main/debug.py b/src/flake8/main/debug.py
index e02da6b..1d56189 100644
--- a/src/flake8/main/debug.py
+++ b/src/flake8/main/debug.py
@@ -1,41 +1,38 @@
"""Module containing the logic for our debugging logic."""
from __future__ import print_function
+import argparse
import json
import platform
import entrypoints
-def print_information(
- option, option_string, value, parser, option_manager=None
-):
- """Print debugging information used in bug reports.
+class DebugAction(argparse.Action):
+ """argparse action to print debug information."""
- :param option:
- The optparse Option instance.
- :type option:
- optparse.Option
- :param str option_string:
- The option name
- :param value:
- The value passed to the callback parsed from the command-line
- :param parser:
- The optparse OptionParser instance
- :type parser:
- optparse.OptionParser
- :param option_manager:
- The Flake8 OptionManager instance.
- :type option_manager:
- flake8.options.manager.OptionManager
- """
- if not option_manager.registered_plugins:
+ def __init__(self, *args, **kwargs):
+ """Initialize the action.
+
+ This takes an extra `option_manager` keyword argument which will be
+ used to delay response.
+ """
+ self._option_manager = kwargs.pop("option_manager")
+ super(DebugAction, self).__init__(*args, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ """Perform the argparse action for printing debug information."""
# NOTE(sigmavirus24): Flake8 parses options twice. The first time, we
# will not have any registered plugins. We can skip this one and only
# take action on the second time we're called.
- return
- print(json.dumps(information(option_manager), indent=2, sort_keys=True))
- raise SystemExit(False)
+ if not self._option_manager.registered_plugins:
+ return
+ print(
+ json.dumps(
+ information(self._option_manager), indent=2, sort_keys=True
+ )
+ )
+ raise SystemExit(0)
def information(option_manager):
diff --git a/src/flake8/main/options.py b/src/flake8/main/options.py
index bef5630..75d2c8f 100644
--- a/src/flake8/main/options.py
+++ b/src/flake8/main/options.py
@@ -1,4 +1,6 @@
"""Contains the logic for all of the default options for Flake8."""
+import functools
+
from flake8 import defaults
from flake8.main import debug
from flake8.main import vcs
@@ -83,7 +85,7 @@ def register_default_options(option_manager):
parse_from_config=True,
normalize_paths=True,
help="Comma-separated list of files or directories to exclude."
- " (Default: %default)",
+ " (Default: %(default)s)",
)
add_option(
@@ -103,7 +105,7 @@ def register_default_options(option_manager):
parse_from_config=True,
comma_separated_list=True,
help="Only check for filenames matching the patterns in this comma-"
- "separated list. (Default: %default)",
+ "separated list. (Default: %(default)s)",
)
add_option(
@@ -111,7 +113,7 @@ def register_default_options(option_manager):
default="stdin",
help="The name used when reporting errors from code passed via stdin."
" This is useful for editors piping the file contents to flake8."
- " (Default: %default)",
+ " (Default: %(default)s)",
)
# TODO(sigmavirus24): Figure out --first/--repeat
@@ -142,7 +144,7 @@ def register_default_options(option_manager):
parse_from_config=True,
comma_separated_list=True,
help="Comma-separated list of errors and warnings to ignore (or skip)."
- " For example, ``--ignore=E4,E51,W234``. (Default: %default)",
+ " For example, ``--ignore=E4,E51,W234``. (Default: %(default)s)",
)
add_option(
@@ -168,22 +170,22 @@ def register_default_options(option_manager):
add_option(
"--max-line-length",
- type="int",
+ type=int,
metavar="n",
default=defaults.MAX_LINE_LENGTH,
parse_from_config=True,
help="Maximum allowed line length for the entirety of this run. "
- "(Default: %default)",
+ "(Default: %(default)s)",
)
add_option(
"--max-doc-length",
- type="int",
+ type=int,
metavar="n",
default=None,
parse_from_config=True,
help="Maximum allowed doc line length for the entirety of this run. "
- "(Default: %default)",
+ "(Default: %(default)s)",
)
add_option(
@@ -193,7 +195,7 @@ def register_default_options(option_manager):
parse_from_config=True,
comma_separated_list=True,
help="Comma-separated list of errors and warnings to enable."
- " For example, ``--select=E4,E51,W234``. (Default: %default)",
+ " For example, ``--select=E4,E51,W234``. (Default: %(default)s)",
)
add_option(
@@ -227,7 +229,6 @@ def register_default_options(option_manager):
default="",
parse_from_config=True,
comma_separated_list=True,
- type="string",
help="Enable plugins and extensions that are otherwise disabled "
"by default",
)
@@ -240,10 +241,8 @@ def register_default_options(option_manager):
add_option(
"--install-hook",
- action="callback",
- type="choice",
+ action=vcs.InstallAction,
choices=vcs.choices(),
- callback=vcs.install,
help="Install a hook that is run prior to a commit for the supported "
"version control system.",
)
@@ -251,21 +250,18 @@ def register_default_options(option_manager):
add_option(
"-j",
"--jobs",
- type="string",
default="auto",
parse_from_config=True,
help="Number of subprocesses to use to run checks in parallel. "
'This is ignored on Windows. The default, "auto", will '
"auto-detect the number of processors available to use."
- " (Default: %default)",
+ " (Default: %(default)s)",
)
add_option(
"--output-file",
default=None,
- type="string",
parse_from_config=True,
- # callback=callbacks.redirect_stdout,
help="Redirect report to a file.",
)
@@ -316,8 +312,9 @@ def register_default_options(option_manager):
add_option(
"--bug-report",
- action="callback",
- callback=debug.print_information,
- callback_kwargs={"option_manager": option_manager},
+ action=functools.partial(
+ debug.DebugAction, option_manager=option_manager
+ ),
+ nargs=0,
help="Print information necessary when preparing a bug report",
)
diff --git a/src/flake8/main/vcs.py b/src/flake8/main/vcs.py
index 398643c..cbe6972 100644
--- a/src/flake8/main/vcs.py
+++ b/src/flake8/main/vcs.py
@@ -1,4 +1,6 @@
"""Module containing some of the logic for our VCS installation logic."""
+import argparse
+
from flake8 import exceptions as exc
from flake8.main import git
from flake8.main import mercurial
@@ -11,24 +13,23 @@ from flake8.main import mercurial
_INSTALLERS = {"git": git.install, "mercurial": mercurial.install}
-def install(option, option_string, value, parser):
- """Determine which version control hook to install.
-
- For more information about the callback signature, see:
- https://docs.python.org/3/library/optparse.html#optparse-option-callbacks
- """
- installer = _INSTALLERS[value]
- errored = False
- successful = False
- try:
- successful = installer()
- except exc.HookInstallationError as hook_error:
- print(str(hook_error))
- errored = True
-
- if not successful:
- print("Could not find the {0} directory".format(value))
- raise SystemExit(not successful and errored)
+class InstallAction(argparse.Action):
+ """argparse action to run the hook installation."""
+
+ def __call__(self, parser, namespace, value, option_string=None):
+ """Perform the argparse action for installing vcs hooks."""
+ installer = _INSTALLERS[value]
+ errored = False
+ successful = False
+ try:
+ successful = installer()
+ except exc.HookInstallationError as hook_error:
+ print(str(hook_error))
+ errored = True
+
+ if not successful:
+ print("Could not find the {0} directory".format(value))
+ raise SystemExit(not successful and errored)
def choices():
diff --git a/src/flake8/options/aggregator.py b/src/flake8/options/aggregator.py
index 304f53c..1b9c60c 100644
--- a/src/flake8/options/aggregator.py
+++ b/src/flake8/options/aggregator.py
@@ -21,13 +21,13 @@ def aggregate_options(manager, config_finder, arglist=None, values=None):
The list of arguments to pass to ``manager.parse_args``. In most cases
this will be None so ``parse_args`` uses ``sys.argv``. This is mostly
available to make testing easier.
- :param optparse.Values values:
+ :param argparse.Namespace values:
Previously parsed set of parsed options.
:returns:
Tuple of the parsed options and extra arguments returned by
``manager.parse_args``.
:rtype:
- tuple(optparse.Values, list)
+ tuple(argparse.Namespace, list)
"""
# Get defaults from the option parser
default_values, _ = manager.parse_args([], values=values)
diff --git a/src/flake8/options/config.py b/src/flake8/options/config.py
index 2af8cc8..0a43625 100644
--- a/src/flake8/options/config.py
+++ b/src/flake8/options/config.py
@@ -167,9 +167,6 @@ class MergedConfigParser(object):
dictionaries with the parsed values.
"""
- #: Set of types that should use the
- #: :meth:`~configparser.RawConfigParser.getint` method.
- GETINT_TYPES = {"int", "count"}
#: Set of actions that should use the
#: :meth:`~configparser.RawConfigParser.getbool` method.
GETBOOL_ACTIONS = {"store_true", "store_false"}
@@ -216,10 +213,7 @@ class MergedConfigParser(object):
# Use the appropriate method to parse the config value
method = config_parser.get
- if (
- option.type in self.GETINT_TYPES
- or option.action in self.GETINT_TYPES
- ):
+ if option.type is int or option.action == "count":
method = config_parser.getint
elif option.action in self.GETBOOL_ACTIONS:
method = config_parser.getboolean
diff --git a/src/flake8/options/manager.py b/src/flake8/options/manager.py
index cf94927..219e0ad 100644
--- a/src/flake8/options/manager.py
+++ b/src/flake8/options/manager.py
@@ -1,55 +1,100 @@
"""Option handling and Option management logic."""
+import argparse
import collections
+import functools
import logging
-import optparse # pylint: disable=deprecated-module
-from typing import Dict, List, Optional, Set
+from typing import Any, Dict, List, Optional, Set
from flake8 import utils
LOG = logging.getLogger(__name__)
+_NOARG = object()
+
+
+class _CallbackAction(argparse.Action):
+ """Shim for optparse-style callback actions."""
+
+ def __init__(self, *args, **kwargs):
+ self._callback = kwargs.pop("callback")
+ self._callback_args = kwargs.pop("callback_args", ())
+ self._callback_kwargs = kwargs.pop("callback_kwargs", {})
+ super(_CallbackAction, self).__init__(*args, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ if not values:
+ values = None
+ elif isinstance(values, list) and len(values) > 1:
+ values = tuple(values)
+ self._callback(
+ self,
+ option_string,
+ values,
+ parser,
+ *self._callback_args,
+ **self._callback_kwargs
+ )
+
+
+def _flake8_normalize(value, *args, **kwargs):
+ comma_separated_list = kwargs.pop("comma_separated_list", False)
+ normalize_paths = kwargs.pop("normalize_paths", False)
+ if kwargs:
+ raise TypeError("Unexpected keyword args: {}".format(kwargs))
+
+ if comma_separated_list and isinstance(value, utils.string_types):
+ value = utils.parse_comma_separated_list(value)
+
+ if normalize_paths:
+ if isinstance(value, list):
+ value = utils.normalize_paths(value, *args)
+ else:
+ value = utils.normalize_path(value, *args)
+
+ return value
+
class Option(object):
- """Our wrapper around an optparse.Option object to add features."""
+ """Our wrapper around an argparse argument parsers to add features."""
def __init__(
self,
- short_option_name=None,
- long_option_name=None,
+ short_option_name=_NOARG,
+ long_option_name=_NOARG,
# Options below here are taken from the optparse.Option class
- action=None,
- default=None,
- type=None,
- dest=None,
- nargs=None,
- const=None,
- choices=None,
- callback=None,
- callback_args=None,
- callback_kwargs=None,
- help=None,
- metavar=None,
+ action=_NOARG,
+ default=_NOARG,
+ type=_NOARG,
+ dest=_NOARG,
+ nargs=_NOARG,
+ const=_NOARG,
+ choices=_NOARG,
+ help=_NOARG,
+ metavar=_NOARG,
+ # deprecated optparse-only options
+ callback=_NOARG,
+ callback_args=_NOARG,
+ callback_kwargs=_NOARG,
+ # Options below are taken from argparse.ArgumentParser.add_argument
+ required=_NOARG,
# Options below here are specific to Flake8
parse_from_config=False,
comma_separated_list=False,
normalize_paths=False,
):
- """Initialize an Option instance wrapping optparse.Option.
+ """Initialize an Option instance.
- The following are all passed directly through to optparse.
+ The following are all passed directly through to argparse.
:param str short_option_name:
The short name of the option (e.g., ``-x``). This will be the
- first argument passed to :class:`~optparse.Option`.
+ first argument passed to ``ArgumentParser.add_argument``
:param str long_option_name:
The long name of the option (e.g., ``--xtra-long-option``). This
- will be the second argument passed to :class:`~optparse.Option`.
- :param str action:
- Any action allowed by :mod:`optparse`.
+ will be the second argument passed to
+ ``ArgumentParser.add_argument``
:param default:
Default value of the option.
- :param type:
- Any type allowed by :mod:`optparse`.
:param dest:
Attribute name to store parsed option value as.
:param nargs:
@@ -59,16 +104,33 @@ class Option(object):
conjuntion with ``action="store_const"``.
:param iterable choices:
Possible values for the option.
- :param callable callback:
- Callback used if the action is ``"callback"``.
- :param iterable callback_args:
- Additional positional arguments to the callback callable.
- :param dictionary callback_kwargs:
- Keyword arguments to the callback callable.
:param str help:
Help text displayed in the usage information.
:param str metavar:
Name to use instead of the long option name for help text.
+ :param bool required:
+ Whether this option is required or not.
+
+ The following options may be passed directly through to :mod:`argparse`
+ but may need some massaging.
+
+ :param type:
+ A callable to normalize the type (as is the case in
+ :mod:`argparse`). Deprecated: you can also pass through type
+ strings such as ``'int'`` which are handled by :mod:`optparse`.
+ :param str action:
+ Any action allowed by :mod:`argparse`. Deprecated: this also
+ understands the ``action='callback'`` action from :mod:`optparse`.
+ :param callable callback:
+ Callback used if the action is ``"callback"``. Deprecated: please
+ use ``action=`` instead.
+ :param iterable callback_args:
+ Additional positional arguments to the callback callable.
+ Deprecated: please use ``action=`` instead (probably with
+ ``functools.partial``).
+ :param dictionary callback_kwargs:
+ Keyword arguments to the callback callable. Deprecated: please
+ use ``action=`` instead (probably with ``functools.partial``).
The following parameters are for Flake8's option handling alone.
@@ -81,16 +143,66 @@ class Option(object):
Whether the option is expecting a path or list of paths and should
attempt to normalize the paths to absolute paths.
"""
+ if long_option_name is _NOARG and short_option_name.startswith("--"):
+ short_option_name, long_option_name = _NOARG, short_option_name
+
+ # optparse -> argparse `%default` => `%(default)s`
+ if help is not _NOARG and "%default" in help:
+ LOG.warning(
+ "option %s: please update `help=` text to use %%(default)s "
+ "instead of %%default -- this will be an error in the future",
+ long_option_name,
+ )
+ help = help.replace("%default", "%(default)s")
+
+ # optparse -> argparse for `callback`
+ if action == "callback":
+ LOG.warning(
+ "option %s: please update from optparse `action='callback'` "
+ "to argparse action classes -- this will be an error in the "
+ "future",
+ long_option_name,
+ )
+ action = _CallbackAction
+ if type is _NOARG:
+ nargs = 0
+
+ # optparse -> argparse for `type`
+ if isinstance(type, utils.string_types):
+ LOG.warning(
+ "option %s: please update from optparse string `type=` to "
+ "argparse callable `type=` -- this will be an error in the "
+ "future"
+ )
+ type = {
+ "int": int,
+ "long": int,
+ "string": str,
+ "float": float,
+ "complex": complex,
+ "choice": _NOARG,
+ }[type]
+
+ # flake8 special type normalization
+ if comma_separated_list or normalize_paths:
+ type = functools.partial(
+ _flake8_normalize,
+ comma_separated_list=comma_separated_list,
+ normalize_paths=normalize_paths,
+ )
+
self.short_option_name = short_option_name
self.long_option_name = long_option_name
self.option_args = [
- x for x in (short_option_name, long_option_name) if x is not None
+ x
+ for x in (short_option_name, long_option_name)
+ if x is not _NOARG
]
self.option_kwargs = {
"action": action,
"default": default,
"type": type,
- "dest": self._make_dest(dest),
+ "dest": dest,
"nargs": nargs,
"const": const,
"choices": choices,
@@ -111,7 +223,7 @@ class Option(object):
self.config_name = None # type: Optional[str]
if parse_from_config:
- if not long_option_name:
+ if long_option_name is _NOARG:
raise ValueError(
"When specifying parse_from_config=True, "
"a long_option_name must also be specified."
@@ -120,25 +232,20 @@ class Option(object):
self._opt = None
- def __repr__(self): # noqa: D105
- return (
- "Option({0}, {1}, action={action}, default={default}, "
- "dest={dest}, type={type}, callback={callback}, help={help},"
- " callback={callback}, callback_args={callback_args}, "
- "callback_kwargs={callback_kwargs}, metavar={metavar})"
- ).format(
- self.short_option_name,
- self.long_option_name,
- **self.option_kwargs
- )
-
- def _make_dest(self, dest):
- if dest:
- return dest
+ @property
+ def filtered_option_kwargs(self): # type: () -> Dict[str, Any]
+ """Return any actually-specified arguments."""
+ return {
+ k: v for k, v in self.option_kwargs.items() if v is not _NOARG
+ }
- if self.long_option_name:
- return self.long_option_name[2:].replace("-", "_")
- return self.short_option_name[1]
+ def __repr__(self): # noqa: D105
+ parts = []
+ for arg in self.option_args:
+ parts.append(arg)
+ for k, v in self.filtered_option_kwargs.items():
+ parts.append("{}={!r}".format(k, v))
+ return "Option({})".format(", ".join(parts))
def normalize(self, value, *normalize_args):
"""Normalize the value based on the option configuration."""
@@ -158,11 +265,11 @@ class Option(object):
def normalize_from_setuptools(self, value):
"""Normalize the value received from setuptools."""
value = self.normalize(value)
- if self.type == "int" or self.action == "count":
+ if self.type is int or self.action == "count":
return int(value)
- elif self.type == "float":
+ elif self.type is float:
return float(value)
- elif self.type == "complex":
+ elif self.type is complex:
return complex(value)
if self.action in ("store_true", "store_false"):
value = str(value).upper()
@@ -172,13 +279,14 @@ class Option(object):
return False
return value
+ def to_argparse(self):
+ """Convert a Flake8 Option to argparse ``add_argument`` arguments."""
+ return self.option_args, self.filtered_option_kwargs
+
+ @property
def to_optparse(self):
- """Convert a Flake8 Option to an optparse Option."""
- if self._opt is None:
- self._opt = optparse.Option(
- *self.option_args, **self.option_kwargs
- )
- return self._opt
+ """No longer functional."""
+ raise AttributeError("to_optparse: flake8 now uses argparse")
PluginVersion = collections.namedtuple(
@@ -190,7 +298,10 @@ class OptionManager(object):
"""Manage Options and OptionParser while adding post-processing."""
def __init__(
- self, prog=None, version=None, usage="%prog [options] file file ..."
+ self,
+ prog=None,
+ version=None,
+ usage="%(prog)s [options] file file ...",
):
"""Initialize an instance of an OptionManager.
@@ -201,9 +312,11 @@ class OptionManager(object):
:param str usage:
Basic usage string used by the OptionParser.
"""
- self.parser = optparse.OptionParser(
- prog=prog, version=version, usage=usage
+ self.parser = argparse.ArgumentParser(prog=prog, usage=usage)
+ self.version_action = self.parser.add_argument(
+ "--version", action="version", version=version
)
+ self.parser.add_argument("filenames", nargs="*", metavar="filename")
self.config_options_dict = {} # type: Dict[str, Option]
self.options = [] # type: List[Option]
self.program_name = prog
@@ -226,12 +339,11 @@ class OptionManager(object):
.. note::
``short_option_name`` and ``long_option_name`` may be specified
- positionally as they are with optparse normally.
+ positionally as they are with argparse normally.
"""
- if len(args) == 1 and args[0].startswith("--"):
- args = (None, args[0])
option = Option(*args, **kwargs)
- self.parser.add_option(option.to_optparse())
+ option_args, option_kwargs = option.to_argparse()
+ self.parser.add_argument(*option_args, **option_kwargs)
self.options.append(option)
if option.parse_from_config:
name = option.config_name
@@ -289,12 +401,8 @@ class OptionManager(object):
def update_version_string(self):
"""Update the flake8 version string."""
- self.parser.version = (
- self.version
- + " ("
- + self.generate_versions()
- + ") "
- + utils.get_python_version()
+ self.version_action.version = "{} ({}) {}".format(
+ self.version, self.generate_versions(), utils.get_python_version()
)
def generate_epilog(self):
@@ -304,18 +412,13 @@ class OptionManager(object):
plugin_version_format
)
- def _normalize(self, options):
- for option in self.options:
- old_value = getattr(options, option.dest)
- setattr(options, option.dest, option.normalize(old_value))
-
def parse_args(self, args=None, values=None):
"""Proxy to calling the OptionParser's parse_args method."""
self.generate_epilog()
self.update_version_string()
- options, xargs = self.parser.parse_args(args, values)
- self._normalize(options)
- return options, xargs
+ args = self.parser.parse_args(args, values)
+ # TODO: refactor callers to not need this
+ return args, args.filenames
def parse_known_args(self, args=None, values=None):
"""Parse only the known arguments from the argument values.
@@ -325,33 +428,8 @@ class OptionManager(object):
"""
self.generate_epilog()
self.update_version_string()
- # Taken from optparse.OptionParser.parse_args
- rargs = self.parser._get_args(args)
- if values is None:
- values = self.parser.get_default_values()
-
- self.parser.rargs = rargs
- largs = [] # type: List[str]
- self.parser.values = values
-
- while rargs:
- # NOTE(sigmavirus24): If we only care about *known* options, then
- # we should just shift the bad option over to the largs list and
- # carry on.
- # Unfortunately, we need to rely on a private method here.
- try:
- self.parser._process_args(largs, rargs, values)
- except (
- optparse.BadOptionError,
- optparse.OptionValueError,
- ) as err:
- # TODO: https://gitlab.com/pycqa/flake8/issues/541
- largs.append(err.opt_str) # type: ignore
-
- args = largs + rargs
- options, xargs = self.parser.check_values(values, args)
- self._normalize(options)
- return options, xargs
+ args, rest = self.parser.parse_known_args(args, values)
+ return args, rest
def register_plugin(self, name, version, local=False):
"""Register a plugin relying on the OptionManager.
diff --git a/src/flake8/plugins/pyflakes.py b/src/flake8/plugins/pyflakes.py
index 018d1c9..b216b62 100644
--- a/src/flake8/plugins/pyflakes.py
+++ b/src/flake8/plugins/pyflakes.py
@@ -118,7 +118,6 @@ class FlakesChecker(pyflakes.checker.Checker):
comma_separated_list=True,
normalize_paths=True,
help="Run doctests only on these files",
- type="string",
)
parser.add_option(
"--exclude-from-doctest",
@@ -128,7 +127,6 @@ class FlakesChecker(pyflakes.checker.Checker):
comma_separated_list=True,
normalize_paths=True,
help="Skip these files when running doctests",
- type="string",
)
@classmethod
diff --git a/src/flake8/style_guide.py b/src/flake8/style_guide.py
index b644f8b..8d3c8cf 100644
--- a/src/flake8/style_guide.py
+++ b/src/flake8/style_guide.py
@@ -349,7 +349,7 @@ class StyleGuideManager(object):
:param options:
The original options parsed from the CLI and config file.
:type options:
- :class:`~optparse.Values`
+ :class:`~argparse.Namespace`
:returns:
A copy of the default style guide with overridden values.
:rtype:
diff --git a/tests/integration/test_main.py b/tests/integration/test_main.py
index ccc70e1..001f1ff 100644
--- a/tests/integration/test_main.py
+++ b/tests/integration/test_main.py
@@ -1,10 +1,18 @@
"""Integration tests for the main entrypoint of flake8."""
+import json
import os
import mock
+import pytest
from flake8 import utils
-from flake8.main import application
+from flake8.main import cli
+
+
+def _call_main(argv, retv=0):
+ with pytest.raises(SystemExit) as excinfo:
+ cli.main(argv)
+ assert excinfo.value.code == retv
def test_diff_option(tmpdir, capsys):
@@ -36,9 +44,7 @@ index d64ac39..7d943de 100644
with mock.patch.object(utils, 'stdin_get_value', return_value=diff):
with tmpdir.as_cwd():
tmpdir.join('t.py').write(t_py_contents)
-
- app = application.Application()
- app.run(['--diff'])
+ _call_main(['--diff'], retv=1)
out, err = capsys.readouterr()
assert out == "t.py:8:1: F821 undefined name 'y'\n"
@@ -49,9 +55,7 @@ def test_statistics_option(tmpdir, capsys):
"""Ensure that `flake8 --statistics` works."""
with tmpdir.as_cwd():
tmpdir.join('t.py').write('import os\nimport sys\n')
-
- app = application.Application()
- app.run(['--statistics', 't.py'])
+ _call_main(['--statistics', 't.py'], retv=1)
out, err = capsys.readouterr()
assert out == '''\
@@ -68,7 +72,7 @@ def test_extend_exclude(tmpdir, capsys):
tmpdir.mkdir(d).join('t.py').write('import os\nimport sys\n')
with tmpdir.as_cwd():
- application.Application().run(['--extend-exclude=vendor,legacy'])
+ _call_main(['--extend-exclude=vendor,legacy'], retv=1)
out, err = capsys.readouterr()
expected_out = '''\
@@ -90,9 +94,7 @@ per-file-ignores =
with tmpdir.as_cwd():
tmpdir.join('setup.cfg').write(setup_cfg)
-
- app = application.Application()
- app.run(['.'])
+ _call_main(['.'], retv=1)
out, err = capsys.readouterr()
assert out == '''\
@@ -111,10 +113,16 @@ def test_tokenization_error_but_not_syntax_error(tmpdir, capsys):
with tmpdir.as_cwd():
# this is a crash in the tokenizer, but not in the ast
tmpdir.join('t.py').write("b'foo' \\\n")
-
- app = application.Application()
- app.run(['t.py'])
+ _call_main(['t.py'], retv=1)
out, err = capsys.readouterr()
assert out == 't.py:1:1: E902 TokenError: EOF in multi-line statement\n'
assert err == ''
+
+
+def test_bug_report_successful(capsys):
+ """Test that --bug-report does not crash."""
+ _call_main(['--bug-report'])
+ out, err = capsys.readouterr()
+ assert json.loads(out)
+ assert err == ''
diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py
index d570564..3541ec3 100644
--- a/tests/unit/conftest.py
+++ b/tests/unit/conftest.py
@@ -1,5 +1,5 @@
"""Shared fixtures between unit tests."""
-import optparse
+import argparse
import pytest
@@ -11,7 +11,7 @@ def options_from(**kwargs):
kwargs.setdefault('max_doc_length', None)
kwargs.setdefault('verbose', False)
kwargs.setdefault('stdin_display_name', 'stdin')
- return optparse.Values(kwargs)
+ return argparse.Namespace(**kwargs)
@pytest.fixture
diff --git a/tests/unit/test_application.py b/tests/unit/test_application.py
index cb8372b..8e5c9d5 100644
--- a/tests/unit/test_application.py
+++ b/tests/unit/test_application.py
@@ -1,5 +1,5 @@
"""Tests for the Application class."""
-import optparse
+import argparse
import sys
import mock
@@ -9,12 +9,12 @@ from flake8.main import application as app
def options(**kwargs):
- """Generate optparse.Values for our Application."""
+ """Generate argparse.Namespace for our Application."""
kwargs.setdefault('verbose', 0)
kwargs.setdefault('output_file', None)
kwargs.setdefault('count', False)
kwargs.setdefault('exit_zero', False)
- return optparse.Values(kwargs)
+ return argparse.Namespace(**kwargs)
@pytest.fixture
diff --git a/tests/unit/test_base_formatter.py b/tests/unit/test_base_formatter.py
index ee589aa..7a85554 100644
--- a/tests/unit/test_base_formatter.py
+++ b/tests/unit/test_base_formatter.py
@@ -1,5 +1,5 @@
"""Tests for the BaseFormatter object."""
-import optparse
+import argparse
import mock
import pytest
@@ -9,10 +9,10 @@ from flake8.formatting import base
def options(**kwargs):
- """Create an optparse.Values instance."""
+ """Create an argparse.Namespace instance."""
kwargs.setdefault('output_file', None)
kwargs.setdefault('tee', False)
- return optparse.Values(kwargs)
+ return argparse.Namespace(**kwargs)
@pytest.mark.parametrize('filename', [None, 'out.txt'])
diff --git a/tests/unit/test_debug.py b/tests/unit/test_debug.py
index 2b7995d..c973abb 100644
--- a/tests/unit/test_debug.py
+++ b/tests/unit/test_debug.py
@@ -73,9 +73,10 @@ def test_information(system, pyversion, pyimpl):
def test_print_information_no_plugins(dumps, information, print_mock):
"""Verify we print and exit only when we have plugins."""
option_manager = mock.Mock(registered_plugins=set())
- assert debug.print_information(
- None, None, None, None, option_manager=option_manager,
- ) is None
+ action = debug.DebugAction(
+ "--bug-report", dest="bug_report", option_manager=option_manager,
+ )
+ assert action(None, None, None, None) is None
assert dumps.called is False
assert information.called is False
assert print_mock.called is False
@@ -91,10 +92,11 @@ def test_print_information(dumps, information, print_mock):
manager.PluginVersion('mccabe', '0.5.9', False),
]
option_manager = mock.Mock(registered_plugins=set(plugins))
+ action = debug.DebugAction(
+ "--bug-report", dest="bug_report", option_manager=option_manager,
+ )
with pytest.raises(SystemExit):
- debug.print_information(
- None, None, None, None, option_manager=option_manager,
- )
+ action(None, None, None, None)
print_mock.assert_called_once_with('{}')
dumps.assert_called_once_with({}, indent=2, sort_keys=True)
information.assert_called_once_with(option_manager)
diff --git a/tests/unit/test_decision_engine.py b/tests/unit/test_decision_engine.py
index 635c9d8..7d92309 100644
--- a/tests/unit/test_decision_engine.py
+++ b/tests/unit/test_decision_engine.py
@@ -1,5 +1,5 @@
"""Tests for the flake8.style_guide.DecisionEngine class."""
-import optparse
+import argparse
import pytest
@@ -8,14 +8,14 @@ from flake8 import style_guide
def create_options(**kwargs):
- """Create and return an instance of optparse.Values."""
+ """Create and return an instance of argparse.Namespace."""
kwargs.setdefault('select', [])
kwargs.setdefault('extended_default_select', [])
kwargs.setdefault('ignore', [])
kwargs.setdefault('extend_ignore', [])
kwargs.setdefault('disable_noqa', False)
kwargs.setdefault('enable_extensions', [])
- return optparse.Values(kwargs)
+ return argparse.Namespace(**kwargs)
@pytest.mark.parametrize('ignore_list,extend_ignore,error_code', [
diff --git a/tests/unit/test_filenameonly_formatter.py b/tests/unit/test_filenameonly_formatter.py
index b423ee3..8d0c88f 100644
--- a/tests/unit/test_filenameonly_formatter.py
+++ b/tests/unit/test_filenameonly_formatter.py
@@ -1,15 +1,15 @@
"""Tests for the FilenameOnly formatter object."""
-import optparse
+import argparse
from flake8 import style_guide
from flake8.formatting import default
def options(**kwargs):
- """Create an optparse.Values instance."""
+ """Create an argparse.Namespace instance."""
kwargs.setdefault('output_file', None)
kwargs.setdefault('tee', False)
- return optparse.Values(kwargs)
+ return argparse.Namespace(**kwargs)
def test_caches_filenames_already_printed():
diff --git a/tests/unit/test_merged_config_parser.py b/tests/unit/test_merged_config_parser.py
index 37d5e6f..1112a0d 100644
--- a/tests/unit/test_merged_config_parser.py
+++ b/tests/unit/test_merged_config_parser.py
@@ -215,7 +215,7 @@ def test_parsed_hyphenated_and_underscored_names(
max_line_length in our config files.
"""
optmanager.add_option('--max-line-length', parse_from_config=True,
- type='int')
+ type=int)
optmanager.add_option('--enable-extensions', parse_from_config=True,
comma_separated_list=True)
parser = config.MergedConfigParser(optmanager, config_finder)
diff --git a/tests/unit/test_nothing_formatter.py b/tests/unit/test_nothing_formatter.py
index a1fd683..85a2e76 100644
--- a/tests/unit/test_nothing_formatter.py
+++ b/tests/unit/test_nothing_formatter.py
@@ -1,15 +1,15 @@
"""Tests for the Nothing formatter obbject."""
-import optparse
+import argparse
from flake8 import style_guide
from flake8.formatting import default
def options(**kwargs):
- """Create an optparse.Values instance."""
+ """Create an argparse.Namespace instance."""
kwargs.setdefault('output_file', None)
kwargs.setdefault('tee', False)
- return optparse.Values(kwargs)
+ return argparse.Namespace(**kwargs)
def test_format_returns_nothing():
diff --git a/tests/unit/test_option.py b/tests/unit/test_option.py
index e931d13..d4607b3 100644
--- a/tests/unit/test_option.py
+++ b/tests/unit/test_option.py
@@ -1,12 +1,14 @@
"""Unit tests for flake8.options.manager.Option."""
+import functools
+
import mock
import pytest
from flake8.options import manager
-def test_to_optparse():
- """Test conversion to an optparse.Option class."""
+def test_to_argparse():
+ """Test conversion to an argparse arguments."""
opt = manager.Option(
short_option_name='-t',
long_option_name='--test',
@@ -17,45 +19,26 @@ def test_to_optparse():
assert opt.normalize_paths is True
assert opt.parse_from_config is True
- optparse_opt = opt.to_optparse()
- assert not hasattr(optparse_opt, 'parse_from_config')
- assert not hasattr(optparse_opt, 'normalize_paths')
- assert optparse_opt.action == 'count'
+ args, kwargs = opt.to_argparse()
+ assert args == ['-t', '--test']
+ assert kwargs == {'action': 'count', 'type': mock.ANY}
+ assert isinstance(kwargs['type'], functools.partial)
-@pytest.mark.parametrize('opttype,str_val,expected', [
- ('float', '2', 2.0),
- ('complex', '2', (2 + 0j)),
-])
-def test_to_support_optparses_standard_types(opttype, str_val, expected):
- """Show that optparse converts float and complex types correctly."""
- opt = manager.Option('-t', '--test', type=opttype)
- assert opt.normalize_from_setuptools(str_val) == expected
+def test_to_optparse():
+ """Test that .to_optparse() produces a useful error message."""
+ with pytest.raises(AttributeError) as excinfo:
+ manager.Option('--foo').to_optparse()
+ msg, = excinfo.value.args
+ assert msg == 'to_optparse: flake8 now uses argparse'
-@mock.patch('optparse.Option')
-def test_to_optparse_creates_an_option_as_we_expect(Option): # noqa: N803
- """Show that we pass all keyword args to optparse.Option."""
+def test_to_argparse_creates_an_option_as_we_expect():
+ """Show that we pass all keyword args to argparse."""
opt = manager.Option('-t', '--test', action='count')
- opt.to_optparse()
- option_kwargs = {
- 'action': 'count',
- 'default': None,
- 'type': None,
- 'dest': 'test',
- 'nargs': None,
- 'const': None,
- 'choices': None,
- 'callback': None,
- 'callback_args': None,
- 'callback_kwargs': None,
- 'help': None,
- 'metavar': None,
- }
-
- Option.assert_called_once_with(
- '-t', '--test', **option_kwargs
- )
+ args, kwargs = opt.to_argparse()
+ assert args == ['-t', '--test']
+ assert kwargs == {'action': 'count'}
def test_config_name_generation():
diff --git a/tests/unit/test_option_manager.py b/tests/unit/test_option_manager.py
index df1f9a2..ebcd9e7 100644
--- a/tests/unit/test_option_manager.py
+++ b/tests/unit/test_option_manager.py
@@ -1,5 +1,5 @@
"""Unit tests for flake.options.manager.OptionManager."""
-import optparse
+import argparse
import os
import mock
@@ -19,8 +19,7 @@ def optmanager():
def test_option_manager_creates_option_parser(optmanager):
"""Verify that a new manager creates a new parser."""
- assert optmanager.parser is not None
- assert isinstance(optmanager.parser, optparse.OptionParser) is True
+ assert isinstance(optmanager.parser, argparse.ArgumentParser)
def test_add_option_short_option_only(optmanager):
@@ -38,7 +37,7 @@ def test_add_option_long_option_only(optmanager):
assert optmanager.config_options_dict == {}
optmanager.add_option('--long', help='Test long opt')
- assert optmanager.options[0].short_option_name is None
+ assert optmanager.options[0].short_option_name is manager._NOARG
assert optmanager.options[0].long_option_name == '--long'
@@ -171,7 +170,7 @@ def test_generate_versions_with_format_string(optmanager):
def test_update_version_string(optmanager):
"""Verify we update the version string idempotently."""
assert optmanager.version == TEST_VERSION
- assert optmanager.parser.version == TEST_VERSION
+ assert optmanager.version_action.version == TEST_VERSION
optmanager.registered_plugins = [
manager.PluginVersion('Testing 100', '0.0.0', False),
@@ -182,7 +181,7 @@ def test_update_version_string(optmanager):
optmanager.update_version_string()
assert optmanager.version == TEST_VERSION
- assert (optmanager.parser.version == TEST_VERSION
+ assert (optmanager.version_action.version == TEST_VERSION
+ ' (Testing 100: 0.0.0, Testing 101: 0.0.0, Testing 300: 0.0.0) '
+ utils.get_python_version())
@@ -211,9 +210,7 @@ def test_extend_default_ignore(optmanager):
assert optmanager.extended_default_ignore == set()
optmanager.extend_default_ignore(['T100', 'T101', 'T102'])
- assert optmanager.extended_default_ignore == {'T100',
- 'T101',
- 'T102'}
+ assert optmanager.extended_default_ignore == {'T100', 'T101', 'T102'}
def test_parse_known_args(optmanager):
@@ -222,3 +219,91 @@ def test_parse_known_args(optmanager):
optmanager.parse_known_args(['--max-complexity', '5'])
assert sysexit.called is False
+
+
+def test_optparse_normalize_callback_option_legacy(optmanager):
+ """Test the optparse shim for `callback=`."""
+ callback_foo = mock.Mock()
+ optmanager.add_option(
+ '--foo',
+ action='callback',
+ callback=callback_foo,
+ callback_args=(1, 2),
+ callback_kwargs={'a': 'b'},
+ )
+ callback_bar = mock.Mock()
+ optmanager.add_option(
+ '--bar',
+ action='callback',
+ type='string',
+ callback=callback_bar,
+ )
+ callback_baz = mock.Mock()
+ optmanager.add_option(
+ '--baz',
+ action='callback',
+ type='string',
+ nargs=2,
+ callback=callback_baz,
+ )
+
+ optmanager.parse_args(['--foo', '--bar', 'bararg', '--baz', '1', '2'])
+
+ callback_foo.assert_called_once_with(
+ mock.ANY, # the option / action instance
+ '--foo',
+ None,
+ mock.ANY, # the OptionParser / ArgumentParser
+ 1,
+ 2,
+ a='b',
+ )
+ callback_bar.assert_called_once_with(
+ mock.ANY, # the option / action instance
+ '--bar',
+ 'bararg',
+ mock.ANY, # the OptionParser / ArgumentParser
+ )
+ callback_baz.assert_called_once_with(
+ mock.ANY, # the option / action instance
+ '--baz',
+ ('1', '2'),
+ mock.ANY, # the OptionParser / ArgumentParser
+ )
+
+
+@pytest.mark.parametrize(
+ ('type_s', 'input_val', 'expected'),
+ (
+ ('int', '5', 5),
+ ('long', '6', 6),
+ ('string', 'foo', 'foo'),
+ ('float', '1.5', 1.5),
+ ('complex', '1+5j', 1 + 5j),
+ ),
+)
+def test_optparse_normalize_types(optmanager, type_s, input_val, expected):
+ """Test the optparse shim for type="typename"."""
+ optmanager.add_option('--foo', type=type_s)
+ opts, args = optmanager.parse_args(['--foo', input_val])
+ assert opts.foo == expected
+
+
+def test_optparse_normalize_choice_type(optmanager):
+ """Test the optparse shim for type="choice"."""
+ optmanager.add_option('--foo', type='choice', choices=('1', '2', '3'))
+ opts, args = optmanager.parse_args(['--foo', '1'])
+ assert opts.foo == '1'
+ # fails to parse
+ with pytest.raises(SystemExit):
+ optmanager.parse_args(['--foo', '4'])
+
+
+def test_optparse_normalize_help(optmanager, capsys):
+ """Test the optparse shim for %default in help text."""
+ optmanager.add_option('--foo', default='bar', help='default: %default')
+ with pytest.raises(SystemExit):
+ optmanager.parse_args(['--help'])
+ out, err = capsys.readouterr()
+ output = out + err
+ assert 'default: bar' in output
diff --git a/tests/unit/test_plugin.py b/tests/unit/test_plugin.py
index 4d5510f..cf87ea1 100644
--- a/tests/unit/test_plugin.py
+++ b/tests/unit/test_plugin.py
@@ -1,5 +1,5 @@
"""Tests for flake8.plugins.manager.Plugin."""
-import optparse
+import argparse
import mock
import pytest
@@ -124,7 +124,7 @@ def test_provide_options():
entry_point = mock.Mock(spec=['load'])
plugin_obj = mock.Mock(spec_set=['name', 'version', 'add_options',
'parse_options'])
- option_values = optparse.Values({'enable_extensions': []})
+ option_values = argparse.Namespace(enable_extensions=[])
option_manager = mock.Mock()
plugin = manager.Plugin('T000', entry_point)
plugin._plugin = plugin_obj
diff --git a/tests/unit/test_style_guide.py b/tests/unit/test_style_guide.py
index 46ab28e..38121c1 100644
--- a/tests/unit/test_style_guide.py
+++ b/tests/unit/test_style_guide.py
@@ -1,5 +1,5 @@
"""Tests for the flake8.style_guide.StyleGuide class."""
-import optparse
+import argparse
import mock
import pytest
@@ -11,7 +11,7 @@ from flake8.formatting import base
def create_options(**kwargs):
- """Create and return an instance of optparse.Values."""
+ """Create and return an instance of argparse.Namespace."""
kwargs.setdefault('select', [])
kwargs.setdefault('extended_default_select', [])
kwargs.setdefault('ignore', [])
@@ -19,7 +19,7 @@ def create_options(**kwargs):
kwargs.setdefault('disable_noqa', False)
kwargs.setdefault('enable_extensions', [])
kwargs.setdefault('per_file_ignores', [])
- return optparse.Values(kwargs)
+ return argparse.Namespace(**kwargs)
def test_handle_error_does_not_raise_type_errors():
diff --git a/tox.ini b/tox.ini
index 6765fd8..b7d4fc0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,9 +8,9 @@ deps =
pytest!=3.0.5
coverage
commands =
- coverage run --parallel-mode -m pytest {posargs}
+ coverage run -m pytest {posargs}
coverage combine
- coverage report -m
+ coverage report
[testenv:venv]
deps =