Merge branch 'argparse' into 'master'

move from optparse to argparse

See merge request pycqa/flake8!341
This commit is contained in:
Anthony Sottile 2019-08-18 22:08:13 +00:00
commit f265b2275b
30 changed files with 464 additions and 302 deletions

View file

@ -1,11 +1,31 @@
[run] [run]
parallel = True
branch = True branch = True
source = source =
flake8 flake8
omit =
# Don't complain if non-runnable code isn't run
*/__main__.py
[paths] [paths]
source = source =
src/flake8 src/flake8
.tox/*/lib/python*/site-packages/flake8 .tox/*/lib/python*/site-packages/flake8
.tox/pypy/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:

View file

@ -354,10 +354,6 @@ max-bool-expr=5
[IMPORTS] [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 # Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled) # given file (report RP0402 must not be disabled)
import-graph= import-graph=

View file

@ -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 to ``add_option``, specifically ``parse_from_config`` which is a boolean
value. 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 The first abstraction is the :class:`flake8.options.manager.Option` class. The
second is the :class:`flake8.options.manager.OptionManager`. In fact, we add second is the :class:`flake8.options.manager.OptionManager`. In fact, we add
three new parameters: three new parameters:
@ -219,7 +219,7 @@ API Documentation
.. autofunction:: flake8.options.aggregator.aggregate_options .. autofunction:: flake8.options.aggregator.aggregate_options
.. autoclass:: flake8.options.manager.Option .. autoclass:: flake8.options.manager.Option
:members: __init__, normalize, to_optparse :members: __init__, normalize, to_argparse
.. autoclass:: flake8.options.manager.OptionManager .. autoclass:: flake8.options.manager.OptionManager
:members: :members:

View file

@ -72,7 +72,7 @@ Any plugin that has callable attributes ``add_options`` and
Your ``add_options`` function should expect to receive an instance of Your ``add_options`` function should expect to receive an instance of
|OptionManager|. An |OptionManager| instance behaves very similarly to |OptionManager|. An |OptionManager| instance behaves very similarly to
:class:`optparse.OptionParser`. It, however, uses the layer that |Flake8| has :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| :meth:`~flake8.options.manager.OptionManager.add_option` creates an |Option|
which accepts the same parameters as :mod:`optparse` as well as three extra which accepts the same parameters as :mod:`optparse` as well as three extra
boolean parameters: boolean parameters:
@ -115,13 +115,13 @@ couple examples from |Flake8|. In each example, we will have
'--max-line-length', type='int', metavar='n', '--max-line-length', type='int', metavar='n',
default=defaults.MAX_LINE_LENGTH, parse_from_config=True, default=defaults.MAX_LINE_LENGTH, parse_from_config=True,
help='Maximum allowed line length for the entirety of this run. ' 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 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 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 provide a default, we take advantage of :mod:`argparse`\ 's willingness to
display that in the help text with ``%default``. display that in the help text with ``%(default)s``.
.. code-block:: python .. code-block:: python
@ -129,7 +129,7 @@ display that in the help text with ``%default``.
'--select', metavar='errors', default='', '--select', metavar='errors', default='',
parse_from_config=True, comma_separated_list=True, parse_from_config=True, comma_separated_list=True,
help='Comma-separated list of errors and warnings to enable.' 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 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, comma_separated_list=True, parse_from_config=True,
normalize_paths=True, normalize_paths=True,
help='Comma-separated list of files or directories to exclude.' 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 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 For information about other parameters to
:meth:`~flake8.options.manager.OptionManager.add_option` refer to the :meth:`~flake8.options.manager.OptionManager.add_option` refer to the
documentation of :mod:`optparse`. documentation of :mod:`argparse`.
Accessing Parsed Options Accessing Parsed Options
@ -160,10 +160,10 @@ Accessing Parsed Options
When a plugin has a callable ``parse_options`` attribute, |Flake8| will call When a plugin has a callable ``parse_options`` attribute, |Flake8| will call
it and attempt to provide the |OptionManager| instance, the parsed options it and attempt to provide the |OptionManager| instance, the parsed options
which will be an instance of :class:`optparse.Values`, and the extra arguments which will be an instance of :class:`argparse.Namespace`, and the extra
that were not parsed by the |OptionManager|. If that fails, we will just pass arguments that were not parsed by the |OptionManager|. If that fails, we will
the :class:`optparse.Values`. In other words, your ``parse_options`` just pass the :class:`argparse.Namespace`. In other words, your
callable will have one of the following signatures: ``parse_options`` callable will have one of the following signatures:
.. code-block:: python .. code-block:: python

View file

@ -3,6 +3,7 @@
Previously, users would import :func:`get_style_guide` from ``flake8.engine``. 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. In 3.0 we no longer have an "engine" module but we maintain the API from it.
""" """
import argparse
import logging import logging
import os.path import os.path
@ -72,10 +73,10 @@ class StyleGuide(object):
self._file_checker_manager = application.file_checker_manager self._file_checker_manager = application.file_checker_manager
@property @property
def options(self): def options(self): # type: () -> argparse.Namespace
"""Return application's options. """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 return self._application.options

View file

@ -367,7 +367,7 @@ class FileChecker(object):
:param options: :param options:
Parsed option values from config and command-line. Parsed option values from config and command-line.
:type options: :type options:
optparse.Values argparse.Namespace
""" """
self.options = options self.options = options
self.filename = filename self.filename = filename

View file

@ -1,7 +1,7 @@
"""The base class and interface for all formatting plugins.""" """The base class and interface for all formatting plugins."""
from __future__ import print_function from __future__ import print_function
import optparse import argparse
from typing import IO, List, Optional, Tuple from typing import IO, List, Optional, Tuple
if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2 if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
@ -32,15 +32,17 @@ class BaseFormatter(object):
""" """
def __init__(self, options): def __init__(self, options):
# type: (optparse.Values) -> None # type: (argparse.Namespace) -> None
"""Initialize with the options parsed from config and cli. """Initialize with the options parsed from config and cli.
This also calls a hook, :meth:`after_init`, so subclasses do not need This also calls a hook, :meth:`after_init`, so subclasses do not need
to call super to call this method. to call super to call this method.
:param optparse.Values options: :param options:
User specified configuration parsed from both configuration files User specified configuration parsed from both configuration files
and the command-line interface. and the command-line interface.
:type options:
:class:`argparse.Namespace`
""" """
self.options = options self.options = options
self.filename = options.output_file self.filename = options.output_file

View file

@ -1,8 +1,8 @@
"""Module containing the application logic for Flake8.""" """Module containing the application logic for Flake8."""
from __future__ import print_function from __future__ import print_function
import argparse
import logging import logging
import optparse
import sys import sys
import time import time
from typing import Dict, List, Optional, Set from typing import Dict, List, Optional, Set
@ -52,8 +52,8 @@ class Application(object):
) )
options.register_default_options(self.option_manager) options.register_default_options(self.option_manager)
#: The preliminary options parsed from CLI before plugins are loaded, #: The preliminary options parsed from CLI before plugins are loaded,
#: into a :class:`optparse.Values` instance #: into a :class:`argparse.Namespace` instance
self.prelim_opts = None # type: optparse.Values self.prelim_opts = None # type: argparse.Namespace
#: The preliminary arguments parsed from CLI before plugins are loaded #: The preliminary arguments parsed from CLI before plugins are loaded
self.prelim_args = None # type: List[str] self.prelim_args = None # type: List[str]
#: The instance of :class:`flake8.options.config.ConfigFileFinder` #: The instance of :class:`flake8.options.config.ConfigFileFinder`
@ -77,8 +77,8 @@ class Application(object):
self.file_checker_manager = None # type: checker.Manager self.file_checker_manager = None # type: checker.Manager
#: The user-supplied options parsed into an instance of #: The user-supplied options parsed into an instance of
#: :class:`optparse.Values` #: :class:`argparse.Namespace`
self.options = None # type: optparse.Values self.options = None # type: argparse.Namespace
#: The left over arguments that were not parsed by #: The left over arguments that were not parsed by
#: :attr:`option_manager` #: :attr:`option_manager`
self.args = None # type: List[str] self.args = None # type: List[str]
@ -117,7 +117,7 @@ class Application(object):
# printing the version until we aggregate options from config files # printing the version until we aggregate options from config files
# and the command-line. First, let's clone our arguments on the CLI, # 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 # 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 # do not need to worry and we can continue. If it is, we successfully
# defer printing the version until just a little bit later. # defer printing the version until just a little bit later.
# Similarly we have to defer printing the help text until later. # Similarly we have to defer printing the help text until later.

View file

@ -1,41 +1,38 @@
"""Module containing the logic for our debugging logic.""" """Module containing the logic for our debugging logic."""
from __future__ import print_function from __future__ import print_function
import argparse
import json import json
import platform import platform
import entrypoints import entrypoints
def print_information( class DebugAction(argparse.Action):
option, option_string, value, parser, option_manager=None """argparse action to print debug information."""
):
"""Print debugging information used in bug reports.
:param option: def __init__(self, *args, **kwargs):
The optparse Option instance. """Initialize the action.
:type option:
optparse.Option This takes an extra `option_manager` keyword argument which will be
:param str option_string: used to delay response.
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: 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 # NOTE(sigmavirus24): Flake8 parses options twice. The first time, we
# will not have any registered plugins. We can skip this one and only # will not have any registered plugins. We can skip this one and only
# take action on the second time we're called. # take action on the second time we're called.
if not self._option_manager.registered_plugins:
return return
print(json.dumps(information(option_manager), indent=2, sort_keys=True)) print(
raise SystemExit(False) json.dumps(
information(self._option_manager), indent=2, sort_keys=True
)
)
raise SystemExit(0)
def information(option_manager): def information(option_manager):

View file

@ -1,4 +1,6 @@
"""Contains the logic for all of the default options for Flake8.""" """Contains the logic for all of the default options for Flake8."""
import functools
from flake8 import defaults from flake8 import defaults
from flake8.main import debug from flake8.main import debug
from flake8.main import vcs from flake8.main import vcs
@ -83,7 +85,7 @@ def register_default_options(option_manager):
parse_from_config=True, parse_from_config=True,
normalize_paths=True, normalize_paths=True,
help="Comma-separated list of files or directories to exclude." help="Comma-separated list of files or directories to exclude."
" (Default: %default)", " (Default: %(default)s)",
) )
add_option( add_option(
@ -103,7 +105,7 @@ def register_default_options(option_manager):
parse_from_config=True, parse_from_config=True,
comma_separated_list=True, comma_separated_list=True,
help="Only check for filenames matching the patterns in this comma-" help="Only check for filenames matching the patterns in this comma-"
"separated list. (Default: %default)", "separated list. (Default: %(default)s)",
) )
add_option( add_option(
@ -111,7 +113,7 @@ def register_default_options(option_manager):
default="stdin", default="stdin",
help="The name used when reporting errors from code passed via stdin." help="The name used when reporting errors from code passed via stdin."
" This is useful for editors piping the file contents to flake8." " This is useful for editors piping the file contents to flake8."
" (Default: %default)", " (Default: %(default)s)",
) )
# TODO(sigmavirus24): Figure out --first/--repeat # TODO(sigmavirus24): Figure out --first/--repeat
@ -142,7 +144,7 @@ def register_default_options(option_manager):
parse_from_config=True, parse_from_config=True,
comma_separated_list=True, comma_separated_list=True,
help="Comma-separated list of errors and warnings to ignore (or skip)." 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( add_option(
@ -168,22 +170,22 @@ def register_default_options(option_manager):
add_option( add_option(
"--max-line-length", "--max-line-length",
type="int", type=int,
metavar="n", metavar="n",
default=defaults.MAX_LINE_LENGTH, default=defaults.MAX_LINE_LENGTH,
parse_from_config=True, parse_from_config=True,
help="Maximum allowed line length for the entirety of this run. " help="Maximum allowed line length for the entirety of this run. "
"(Default: %default)", "(Default: %(default)s)",
) )
add_option( add_option(
"--max-doc-length", "--max-doc-length",
type="int", type=int,
metavar="n", metavar="n",
default=None, default=None,
parse_from_config=True, parse_from_config=True,
help="Maximum allowed doc line length for the entirety of this run. " help="Maximum allowed doc line length for the entirety of this run. "
"(Default: %default)", "(Default: %(default)s)",
) )
add_option( add_option(
@ -193,7 +195,7 @@ def register_default_options(option_manager):
parse_from_config=True, parse_from_config=True,
comma_separated_list=True, comma_separated_list=True,
help="Comma-separated list of errors and warnings to enable." 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( add_option(
@ -227,7 +229,6 @@ def register_default_options(option_manager):
default="", default="",
parse_from_config=True, parse_from_config=True,
comma_separated_list=True, comma_separated_list=True,
type="string",
help="Enable plugins and extensions that are otherwise disabled " help="Enable plugins and extensions that are otherwise disabled "
"by default", "by default",
) )
@ -240,10 +241,8 @@ def register_default_options(option_manager):
add_option( add_option(
"--install-hook", "--install-hook",
action="callback", action=vcs.InstallAction,
type="choice",
choices=vcs.choices(), choices=vcs.choices(),
callback=vcs.install,
help="Install a hook that is run prior to a commit for the supported " help="Install a hook that is run prior to a commit for the supported "
"version control system.", "version control system.",
) )
@ -251,21 +250,18 @@ def register_default_options(option_manager):
add_option( add_option(
"-j", "-j",
"--jobs", "--jobs",
type="string",
default="auto", default="auto",
parse_from_config=True, parse_from_config=True,
help="Number of subprocesses to use to run checks in parallel. " help="Number of subprocesses to use to run checks in parallel. "
'This is ignored on Windows. The default, "auto", will ' 'This is ignored on Windows. The default, "auto", will '
"auto-detect the number of processors available to use." "auto-detect the number of processors available to use."
" (Default: %default)", " (Default: %(default)s)",
) )
add_option( add_option(
"--output-file", "--output-file",
default=None, default=None,
type="string",
parse_from_config=True, parse_from_config=True,
# callback=callbacks.redirect_stdout,
help="Redirect report to a file.", help="Redirect report to a file.",
) )
@ -316,8 +312,9 @@ def register_default_options(option_manager):
add_option( add_option(
"--bug-report", "--bug-report",
action="callback", action=functools.partial(
callback=debug.print_information, debug.DebugAction, option_manager=option_manager
callback_kwargs={"option_manager": option_manager}, ),
nargs=0,
help="Print information necessary when preparing a bug report", help="Print information necessary when preparing a bug report",
) )

View file

@ -1,4 +1,6 @@
"""Module containing some of the logic for our VCS installation logic.""" """Module containing some of the logic for our VCS installation logic."""
import argparse
from flake8 import exceptions as exc from flake8 import exceptions as exc
from flake8.main import git from flake8.main import git
from flake8.main import mercurial from flake8.main import mercurial
@ -11,12 +13,11 @@ from flake8.main import mercurial
_INSTALLERS = {"git": git.install, "mercurial": mercurial.install} _INSTALLERS = {"git": git.install, "mercurial": mercurial.install}
def install(option, option_string, value, parser): class InstallAction(argparse.Action):
"""Determine which version control hook to install. """argparse action to run the hook installation."""
For more information about the callback signature, see: def __call__(self, parser, namespace, value, option_string=None):
https://docs.python.org/3/library/optparse.html#optparse-option-callbacks """Perform the argparse action for installing vcs hooks."""
"""
installer = _INSTALLERS[value] installer = _INSTALLERS[value]
errored = False errored = False
successful = False successful = False

View file

@ -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 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 this will be None so ``parse_args`` uses ``sys.argv``. This is mostly
available to make testing easier. available to make testing easier.
:param optparse.Values values: :param argparse.Namespace values:
Previously parsed set of parsed options. Previously parsed set of parsed options.
:returns: :returns:
Tuple of the parsed options and extra arguments returned by Tuple of the parsed options and extra arguments returned by
``manager.parse_args``. ``manager.parse_args``.
:rtype: :rtype:
tuple(optparse.Values, list) tuple(argparse.Namespace, list)
""" """
# Get defaults from the option parser # Get defaults from the option parser
default_values, _ = manager.parse_args([], values=values) default_values, _ = manager.parse_args([], values=values)

View file

@ -167,9 +167,6 @@ class MergedConfigParser(object):
dictionaries with the parsed values. 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 #: Set of actions that should use the
#: :meth:`~configparser.RawConfigParser.getbool` method. #: :meth:`~configparser.RawConfigParser.getbool` method.
GETBOOL_ACTIONS = {"store_true", "store_false"} GETBOOL_ACTIONS = {"store_true", "store_false"}
@ -216,10 +213,7 @@ class MergedConfigParser(object):
# Use the appropriate method to parse the config value # Use the appropriate method to parse the config value
method = config_parser.get method = config_parser.get
if ( if option.type is int or option.action == "count":
option.type in self.GETINT_TYPES
or option.action in self.GETINT_TYPES
):
method = config_parser.getint method = config_parser.getint
elif option.action in self.GETBOOL_ACTIONS: elif option.action in self.GETBOOL_ACTIONS:
method = config_parser.getboolean method = config_parser.getboolean

View file

@ -1,55 +1,100 @@
"""Option handling and Option management logic.""" """Option handling and Option management logic."""
import argparse
import collections import collections
import functools
import logging import logging
import optparse # pylint: disable=deprecated-module from typing import Any, Dict, List, Optional, Set
from typing import Dict, List, Optional, Set
from flake8 import utils from flake8 import utils
LOG = logging.getLogger(__name__) 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): 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__( def __init__(
self, self,
short_option_name=None, short_option_name=_NOARG,
long_option_name=None, long_option_name=_NOARG,
# Options below here are taken from the optparse.Option class # Options below here are taken from the optparse.Option class
action=None, action=_NOARG,
default=None, default=_NOARG,
type=None, type=_NOARG,
dest=None, dest=_NOARG,
nargs=None, nargs=_NOARG,
const=None, const=_NOARG,
choices=None, choices=_NOARG,
callback=None, help=_NOARG,
callback_args=None, metavar=_NOARG,
callback_kwargs=None, # deprecated optparse-only options
help=None, callback=_NOARG,
metavar=None, callback_args=_NOARG,
callback_kwargs=_NOARG,
# Options below are taken from argparse.ArgumentParser.add_argument
required=_NOARG,
# Options below here are specific to Flake8 # Options below here are specific to Flake8
parse_from_config=False, parse_from_config=False,
comma_separated_list=False, comma_separated_list=False,
normalize_paths=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: :param str short_option_name:
The short name of the option (e.g., ``-x``). This will be the 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: :param str long_option_name:
The long name of the option (e.g., ``--xtra-long-option``). This The long name of the option (e.g., ``--xtra-long-option``). This
will be the second argument passed to :class:`~optparse.Option`. will be the second argument passed to
:param str action: ``ArgumentParser.add_argument``
Any action allowed by :mod:`optparse`.
:param default: :param default:
Default value of the option. Default value of the option.
:param type:
Any type allowed by :mod:`optparse`.
:param dest: :param dest:
Attribute name to store parsed option value as. Attribute name to store parsed option value as.
:param nargs: :param nargs:
@ -59,16 +104,33 @@ class Option(object):
conjuntion with ``action="store_const"``. conjuntion with ``action="store_const"``.
:param iterable choices: :param iterable choices:
Possible values for the option. 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: :param str help:
Help text displayed in the usage information. Help text displayed in the usage information.
:param str metavar: :param str metavar:
Name to use instead of the long option name for help text. 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. 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 Whether the option is expecting a path or list of paths and should
attempt to normalize the paths to absolute paths. 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.short_option_name = short_option_name
self.long_option_name = long_option_name self.long_option_name = long_option_name
self.option_args = [ 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 = { self.option_kwargs = {
"action": action, "action": action,
"default": default, "default": default,
"type": type, "type": type,
"dest": self._make_dest(dest), "dest": dest,
"nargs": nargs, "nargs": nargs,
"const": const, "const": const,
"choices": choices, "choices": choices,
@ -111,7 +223,7 @@ class Option(object):
self.config_name = None # type: Optional[str] self.config_name = None # type: Optional[str]
if parse_from_config: if parse_from_config:
if not long_option_name: if long_option_name is _NOARG:
raise ValueError( raise ValueError(
"When specifying parse_from_config=True, " "When specifying parse_from_config=True, "
"a long_option_name must also be specified." "a long_option_name must also be specified."
@ -120,25 +232,20 @@ class Option(object):
self._opt = None self._opt = None
@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
}
def __repr__(self): # noqa: D105 def __repr__(self): # noqa: D105
return ( parts = []
"Option({0}, {1}, action={action}, default={default}, " for arg in self.option_args:
"dest={dest}, type={type}, callback={callback}, help={help}," parts.append(arg)
" callback={callback}, callback_args={callback_args}, " for k, v in self.filtered_option_kwargs.items():
"callback_kwargs={callback_kwargs}, metavar={metavar})" parts.append("{}={!r}".format(k, v))
).format( return "Option({})".format(", ".join(parts))
self.short_option_name,
self.long_option_name,
**self.option_kwargs
)
def _make_dest(self, dest):
if dest:
return dest
if self.long_option_name:
return self.long_option_name[2:].replace("-", "_")
return self.short_option_name[1]
def normalize(self, value, *normalize_args): def normalize(self, value, *normalize_args):
"""Normalize the value based on the option configuration.""" """Normalize the value based on the option configuration."""
@ -158,11 +265,11 @@ class Option(object):
def normalize_from_setuptools(self, value): def normalize_from_setuptools(self, value):
"""Normalize the value received from setuptools.""" """Normalize the value received from setuptools."""
value = self.normalize(value) value = self.normalize(value)
if self.type == "int" or self.action == "count": if self.type is int or self.action == "count":
return int(value) return int(value)
elif self.type == "float": elif self.type is float:
return float(value) return float(value)
elif self.type == "complex": elif self.type is complex:
return complex(value) return complex(value)
if self.action in ("store_true", "store_false"): if self.action in ("store_true", "store_false"):
value = str(value).upper() value = str(value).upper()
@ -172,13 +279,14 @@ class Option(object):
return False return False
return value 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): def to_optparse(self):
"""Convert a Flake8 Option to an optparse Option.""" """No longer functional."""
if self._opt is None: raise AttributeError("to_optparse: flake8 now uses argparse")
self._opt = optparse.Option(
*self.option_args, **self.option_kwargs
)
return self._opt
PluginVersion = collections.namedtuple( PluginVersion = collections.namedtuple(
@ -190,7 +298,10 @@ class OptionManager(object):
"""Manage Options and OptionParser while adding post-processing.""" """Manage Options and OptionParser while adding post-processing."""
def __init__( 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. """Initialize an instance of an OptionManager.
@ -201,9 +312,11 @@ class OptionManager(object):
:param str usage: :param str usage:
Basic usage string used by the OptionParser. Basic usage string used by the OptionParser.
""" """
self.parser = optparse.OptionParser( self.parser = argparse.ArgumentParser(prog=prog, usage=usage)
prog=prog, version=version, 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.config_options_dict = {} # type: Dict[str, Option]
self.options = [] # type: List[Option] self.options = [] # type: List[Option]
self.program_name = prog self.program_name = prog
@ -226,12 +339,11 @@ class OptionManager(object):
.. note:: .. note::
``short_option_name`` and ``long_option_name`` may be specified ``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) 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) self.options.append(option)
if option.parse_from_config: if option.parse_from_config:
name = option.config_name name = option.config_name
@ -289,12 +401,8 @@ class OptionManager(object):
def update_version_string(self): def update_version_string(self):
"""Update the flake8 version string.""" """Update the flake8 version string."""
self.parser.version = ( self.version_action.version = "{} ({}) {}".format(
self.version self.version, self.generate_versions(), utils.get_python_version()
+ " ("
+ self.generate_versions()
+ ") "
+ utils.get_python_version()
) )
def generate_epilog(self): def generate_epilog(self):
@ -304,18 +412,13 @@ class OptionManager(object):
plugin_version_format 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): def parse_args(self, args=None, values=None):
"""Proxy to calling the OptionParser's parse_args method.""" """Proxy to calling the OptionParser's parse_args method."""
self.generate_epilog() self.generate_epilog()
self.update_version_string() self.update_version_string()
options, xargs = self.parser.parse_args(args, values) args = self.parser.parse_args(args, values)
self._normalize(options) # TODO: refactor callers to not need this
return options, xargs return args, args.filenames
def parse_known_args(self, args=None, values=None): def parse_known_args(self, args=None, values=None):
"""Parse only the known arguments from the argument values. """Parse only the known arguments from the argument values.
@ -325,33 +428,8 @@ class OptionManager(object):
""" """
self.generate_epilog() self.generate_epilog()
self.update_version_string() self.update_version_string()
# Taken from optparse.OptionParser.parse_args args, rest = self.parser.parse_known_args(args, values)
rargs = self.parser._get_args(args) return args, rest
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
def register_plugin(self, name, version, local=False): def register_plugin(self, name, version, local=False):
"""Register a plugin relying on the OptionManager. """Register a plugin relying on the OptionManager.

View file

@ -118,7 +118,6 @@ class FlakesChecker(pyflakes.checker.Checker):
comma_separated_list=True, comma_separated_list=True,
normalize_paths=True, normalize_paths=True,
help="Run doctests only on these files", help="Run doctests only on these files",
type="string",
) )
parser.add_option( parser.add_option(
"--exclude-from-doctest", "--exclude-from-doctest",
@ -128,7 +127,6 @@ class FlakesChecker(pyflakes.checker.Checker):
comma_separated_list=True, comma_separated_list=True,
normalize_paths=True, normalize_paths=True,
help="Skip these files when running doctests", help="Skip these files when running doctests",
type="string",
) )
@classmethod @classmethod

View file

@ -349,7 +349,7 @@ class StyleGuideManager(object):
:param options: :param options:
The original options parsed from the CLI and config file. The original options parsed from the CLI and config file.
:type options: :type options:
:class:`~optparse.Values` :class:`~argparse.Namespace`
:returns: :returns:
A copy of the default style guide with overridden values. A copy of the default style guide with overridden values.
:rtype: :rtype:

View file

@ -1,10 +1,18 @@
"""Integration tests for the main entrypoint of flake8.""" """Integration tests for the main entrypoint of flake8."""
import json
import os import os
import mock import mock
import pytest
from flake8 import utils 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): 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 mock.patch.object(utils, 'stdin_get_value', return_value=diff):
with tmpdir.as_cwd(): with tmpdir.as_cwd():
tmpdir.join('t.py').write(t_py_contents) tmpdir.join('t.py').write(t_py_contents)
_call_main(['--diff'], retv=1)
app = application.Application()
app.run(['--diff'])
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert out == "t.py:8:1: F821 undefined name 'y'\n" 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.""" """Ensure that `flake8 --statistics` works."""
with tmpdir.as_cwd(): with tmpdir.as_cwd():
tmpdir.join('t.py').write('import os\nimport sys\n') tmpdir.join('t.py').write('import os\nimport sys\n')
_call_main(['--statistics', 't.py'], retv=1)
app = application.Application()
app.run(['--statistics', 't.py'])
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert out == '''\ assert out == '''\
@ -68,7 +72,7 @@ def test_extend_exclude(tmpdir, capsys):
tmpdir.mkdir(d).join('t.py').write('import os\nimport sys\n') tmpdir.mkdir(d).join('t.py').write('import os\nimport sys\n')
with tmpdir.as_cwd(): with tmpdir.as_cwd():
application.Application().run(['--extend-exclude=vendor,legacy']) _call_main(['--extend-exclude=vendor,legacy'], retv=1)
out, err = capsys.readouterr() out, err = capsys.readouterr()
expected_out = '''\ expected_out = '''\
@ -90,9 +94,7 @@ per-file-ignores =
with tmpdir.as_cwd(): with tmpdir.as_cwd():
tmpdir.join('setup.cfg').write(setup_cfg) tmpdir.join('setup.cfg').write(setup_cfg)
_call_main(['.'], retv=1)
app = application.Application()
app.run(['.'])
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert out == '''\ assert out == '''\
@ -111,10 +113,16 @@ def test_tokenization_error_but_not_syntax_error(tmpdir, capsys):
with tmpdir.as_cwd(): with tmpdir.as_cwd():
# this is a crash in the tokenizer, but not in the ast # this is a crash in the tokenizer, but not in the ast
tmpdir.join('t.py').write("b'foo' \\\n") tmpdir.join('t.py').write("b'foo' \\\n")
_call_main(['t.py'], retv=1)
app = application.Application()
app.run(['t.py'])
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert out == 't.py:1:1: E902 TokenError: EOF in multi-line statement\n' assert out == 't.py:1:1: E902 TokenError: EOF in multi-line statement\n'
assert err == '' 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 == ''

View file

@ -1,5 +1,5 @@
"""Shared fixtures between unit tests.""" """Shared fixtures between unit tests."""
import optparse import argparse
import pytest import pytest
@ -11,7 +11,7 @@ def options_from(**kwargs):
kwargs.setdefault('max_doc_length', None) kwargs.setdefault('max_doc_length', None)
kwargs.setdefault('verbose', False) kwargs.setdefault('verbose', False)
kwargs.setdefault('stdin_display_name', 'stdin') kwargs.setdefault('stdin_display_name', 'stdin')
return optparse.Values(kwargs) return argparse.Namespace(**kwargs)
@pytest.fixture @pytest.fixture

View file

@ -1,5 +1,5 @@
"""Tests for the Application class.""" """Tests for the Application class."""
import optparse import argparse
import sys import sys
import mock import mock
@ -9,12 +9,12 @@ from flake8.main import application as app
def options(**kwargs): def options(**kwargs):
"""Generate optparse.Values for our Application.""" """Generate argparse.Namespace for our Application."""
kwargs.setdefault('verbose', 0) kwargs.setdefault('verbose', 0)
kwargs.setdefault('output_file', None) kwargs.setdefault('output_file', None)
kwargs.setdefault('count', False) kwargs.setdefault('count', False)
kwargs.setdefault('exit_zero', False) kwargs.setdefault('exit_zero', False)
return optparse.Values(kwargs) return argparse.Namespace(**kwargs)
@pytest.fixture @pytest.fixture

View file

@ -1,5 +1,5 @@
"""Tests for the BaseFormatter object.""" """Tests for the BaseFormatter object."""
import optparse import argparse
import mock import mock
import pytest import pytest
@ -9,10 +9,10 @@ from flake8.formatting import base
def options(**kwargs): def options(**kwargs):
"""Create an optparse.Values instance.""" """Create an argparse.Namespace instance."""
kwargs.setdefault('output_file', None) kwargs.setdefault('output_file', None)
kwargs.setdefault('tee', False) kwargs.setdefault('tee', False)
return optparse.Values(kwargs) return argparse.Namespace(**kwargs)
@pytest.mark.parametrize('filename', [None, 'out.txt']) @pytest.mark.parametrize('filename', [None, 'out.txt'])

View file

@ -73,9 +73,10 @@ def test_information(system, pyversion, pyimpl):
def test_print_information_no_plugins(dumps, information, print_mock): def test_print_information_no_plugins(dumps, information, print_mock):
"""Verify we print and exit only when we have plugins.""" """Verify we print and exit only when we have plugins."""
option_manager = mock.Mock(registered_plugins=set()) option_manager = mock.Mock(registered_plugins=set())
assert debug.print_information( action = debug.DebugAction(
None, None, None, None, option_manager=option_manager, "--bug-report", dest="bug_report", option_manager=option_manager,
) is None )
assert action(None, None, None, None) is None
assert dumps.called is False assert dumps.called is False
assert information.called is False assert information.called is False
assert print_mock.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), manager.PluginVersion('mccabe', '0.5.9', False),
] ]
option_manager = mock.Mock(registered_plugins=set(plugins)) option_manager = mock.Mock(registered_plugins=set(plugins))
with pytest.raises(SystemExit): action = debug.DebugAction(
debug.print_information( "--bug-report", dest="bug_report", option_manager=option_manager,
None, None, None, None, option_manager=option_manager,
) )
with pytest.raises(SystemExit):
action(None, None, None, None)
print_mock.assert_called_once_with('{}') print_mock.assert_called_once_with('{}')
dumps.assert_called_once_with({}, indent=2, sort_keys=True) dumps.assert_called_once_with({}, indent=2, sort_keys=True)
information.assert_called_once_with(option_manager) information.assert_called_once_with(option_manager)

View file

@ -1,5 +1,5 @@
"""Tests for the flake8.style_guide.DecisionEngine class.""" """Tests for the flake8.style_guide.DecisionEngine class."""
import optparse import argparse
import pytest import pytest
@ -8,14 +8,14 @@ from flake8 import style_guide
def create_options(**kwargs): 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('select', [])
kwargs.setdefault('extended_default_select', []) kwargs.setdefault('extended_default_select', [])
kwargs.setdefault('ignore', []) kwargs.setdefault('ignore', [])
kwargs.setdefault('extend_ignore', []) kwargs.setdefault('extend_ignore', [])
kwargs.setdefault('disable_noqa', False) kwargs.setdefault('disable_noqa', False)
kwargs.setdefault('enable_extensions', []) kwargs.setdefault('enable_extensions', [])
return optparse.Values(kwargs) return argparse.Namespace(**kwargs)
@pytest.mark.parametrize('ignore_list,extend_ignore,error_code', [ @pytest.mark.parametrize('ignore_list,extend_ignore,error_code', [

View file

@ -1,15 +1,15 @@
"""Tests for the FilenameOnly formatter object.""" """Tests for the FilenameOnly formatter object."""
import optparse import argparse
from flake8 import style_guide from flake8 import style_guide
from flake8.formatting import default from flake8.formatting import default
def options(**kwargs): def options(**kwargs):
"""Create an optparse.Values instance.""" """Create an argparse.Namespace instance."""
kwargs.setdefault('output_file', None) kwargs.setdefault('output_file', None)
kwargs.setdefault('tee', False) kwargs.setdefault('tee', False)
return optparse.Values(kwargs) return argparse.Namespace(**kwargs)
def test_caches_filenames_already_printed(): def test_caches_filenames_already_printed():

View file

@ -215,7 +215,7 @@ def test_parsed_hyphenated_and_underscored_names(
max_line_length in our config files. max_line_length in our config files.
""" """
optmanager.add_option('--max-line-length', parse_from_config=True, optmanager.add_option('--max-line-length', parse_from_config=True,
type='int') type=int)
optmanager.add_option('--enable-extensions', parse_from_config=True, optmanager.add_option('--enable-extensions', parse_from_config=True,
comma_separated_list=True) comma_separated_list=True)
parser = config.MergedConfigParser(optmanager, config_finder) parser = config.MergedConfigParser(optmanager, config_finder)

View file

@ -1,15 +1,15 @@
"""Tests for the Nothing formatter obbject.""" """Tests for the Nothing formatter obbject."""
import optparse import argparse
from flake8 import style_guide from flake8 import style_guide
from flake8.formatting import default from flake8.formatting import default
def options(**kwargs): def options(**kwargs):
"""Create an optparse.Values instance.""" """Create an argparse.Namespace instance."""
kwargs.setdefault('output_file', None) kwargs.setdefault('output_file', None)
kwargs.setdefault('tee', False) kwargs.setdefault('tee', False)
return optparse.Values(kwargs) return argparse.Namespace(**kwargs)
def test_format_returns_nothing(): def test_format_returns_nothing():

View file

@ -1,12 +1,14 @@
"""Unit tests for flake8.options.manager.Option.""" """Unit tests for flake8.options.manager.Option."""
import functools
import mock import mock
import pytest import pytest
from flake8.options import manager from flake8.options import manager
def test_to_optparse(): def test_to_argparse():
"""Test conversion to an optparse.Option class.""" """Test conversion to an argparse arguments."""
opt = manager.Option( opt = manager.Option(
short_option_name='-t', short_option_name='-t',
long_option_name='--test', long_option_name='--test',
@ -17,45 +19,26 @@ def test_to_optparse():
assert opt.normalize_paths is True assert opt.normalize_paths is True
assert opt.parse_from_config is True assert opt.parse_from_config is True
optparse_opt = opt.to_optparse() args, kwargs = opt.to_argparse()
assert not hasattr(optparse_opt, 'parse_from_config') assert args == ['-t', '--test']
assert not hasattr(optparse_opt, 'normalize_paths') assert kwargs == {'action': 'count', 'type': mock.ANY}
assert optparse_opt.action == 'count' assert isinstance(kwargs['type'], functools.partial)
@pytest.mark.parametrize('opttype,str_val,expected', [ def test_to_optparse():
('float', '2', 2.0), """Test that .to_optparse() produces a useful error message."""
('complex', '2', (2 + 0j)), with pytest.raises(AttributeError) as excinfo:
]) manager.Option('--foo').to_optparse()
def test_to_support_optparses_standard_types(opttype, str_val, expected): msg, = excinfo.value.args
"""Show that optparse converts float and complex types correctly.""" assert msg == 'to_optparse: flake8 now uses argparse'
opt = manager.Option('-t', '--test', type=opttype)
assert opt.normalize_from_setuptools(str_val) == expected
@mock.patch('optparse.Option') def test_to_argparse_creates_an_option_as_we_expect():
def test_to_optparse_creates_an_option_as_we_expect(Option): # noqa: N803 """Show that we pass all keyword args to argparse."""
"""Show that we pass all keyword args to optparse.Option."""
opt = manager.Option('-t', '--test', action='count') opt = manager.Option('-t', '--test', action='count')
opt.to_optparse() args, kwargs = opt.to_argparse()
option_kwargs = { assert args == ['-t', '--test']
'action': 'count', assert 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
)
def test_config_name_generation(): def test_config_name_generation():

View file

@ -1,5 +1,5 @@
"""Unit tests for flake.options.manager.OptionManager.""" """Unit tests for flake.options.manager.OptionManager."""
import optparse import argparse
import os import os
import mock import mock
@ -19,8 +19,7 @@ def optmanager():
def test_option_manager_creates_option_parser(optmanager): def test_option_manager_creates_option_parser(optmanager):
"""Verify that a new manager creates a new parser.""" """Verify that a new manager creates a new parser."""
assert optmanager.parser is not None assert isinstance(optmanager.parser, argparse.ArgumentParser)
assert isinstance(optmanager.parser, optparse.OptionParser) is True
def test_add_option_short_option_only(optmanager): 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 == {} assert optmanager.config_options_dict == {}
optmanager.add_option('--long', help='Test long opt') 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' 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): def test_update_version_string(optmanager):
"""Verify we update the version string idempotently.""" """Verify we update the version string idempotently."""
assert optmanager.version == TEST_VERSION assert optmanager.version == TEST_VERSION
assert optmanager.parser.version == TEST_VERSION assert optmanager.version_action.version == TEST_VERSION
optmanager.registered_plugins = [ optmanager.registered_plugins = [
manager.PluginVersion('Testing 100', '0.0.0', False), manager.PluginVersion('Testing 100', '0.0.0', False),
@ -182,7 +181,7 @@ def test_update_version_string(optmanager):
optmanager.update_version_string() optmanager.update_version_string()
assert optmanager.version == TEST_VERSION 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) ' + ' (Testing 100: 0.0.0, Testing 101: 0.0.0, Testing 300: 0.0.0) '
+ utils.get_python_version()) + utils.get_python_version())
@ -211,9 +210,7 @@ def test_extend_default_ignore(optmanager):
assert optmanager.extended_default_ignore == set() assert optmanager.extended_default_ignore == set()
optmanager.extend_default_ignore(['T100', 'T101', 'T102']) optmanager.extend_default_ignore(['T100', 'T101', 'T102'])
assert optmanager.extended_default_ignore == {'T100', assert optmanager.extended_default_ignore == {'T100', 'T101', 'T102'}
'T101',
'T102'}
def test_parse_known_args(optmanager): def test_parse_known_args(optmanager):
@ -222,3 +219,91 @@ def test_parse_known_args(optmanager):
optmanager.parse_known_args(['--max-complexity', '5']) optmanager.parse_known_args(['--max-complexity', '5'])
assert sysexit.called is False 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

View file

@ -1,5 +1,5 @@
"""Tests for flake8.plugins.manager.Plugin.""" """Tests for flake8.plugins.manager.Plugin."""
import optparse import argparse
import mock import mock
import pytest import pytest
@ -124,7 +124,7 @@ def test_provide_options():
entry_point = mock.Mock(spec=['load']) entry_point = mock.Mock(spec=['load'])
plugin_obj = mock.Mock(spec_set=['name', 'version', 'add_options', plugin_obj = mock.Mock(spec_set=['name', 'version', 'add_options',
'parse_options']) 'parse_options'])
option_values = optparse.Values({'enable_extensions': []}) option_values = argparse.Namespace(enable_extensions=[])
option_manager = mock.Mock() option_manager = mock.Mock()
plugin = manager.Plugin('T000', entry_point) plugin = manager.Plugin('T000', entry_point)
plugin._plugin = plugin_obj plugin._plugin = plugin_obj

View file

@ -1,5 +1,5 @@
"""Tests for the flake8.style_guide.StyleGuide class.""" """Tests for the flake8.style_guide.StyleGuide class."""
import optparse import argparse
import mock import mock
import pytest import pytest
@ -11,7 +11,7 @@ from flake8.formatting import base
def create_options(**kwargs): 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('select', [])
kwargs.setdefault('extended_default_select', []) kwargs.setdefault('extended_default_select', [])
kwargs.setdefault('ignore', []) kwargs.setdefault('ignore', [])
@ -19,7 +19,7 @@ def create_options(**kwargs):
kwargs.setdefault('disable_noqa', False) kwargs.setdefault('disable_noqa', False)
kwargs.setdefault('enable_extensions', []) kwargs.setdefault('enable_extensions', [])
kwargs.setdefault('per_file_ignores', []) kwargs.setdefault('per_file_ignores', [])
return optparse.Values(kwargs) return argparse.Namespace(**kwargs)
def test_handle_error_does_not_raise_type_errors(): def test_handle_error_does_not_raise_type_errors():

View file

@ -8,9 +8,9 @@ deps =
pytest!=3.0.5 pytest!=3.0.5
coverage coverage
commands = commands =
coverage run --parallel-mode -m pytest {posargs} coverage run -m pytest {posargs}
coverage combine coverage combine
coverage report -m coverage report
[testenv:venv] [testenv:venv]
deps = deps =