mirror of
https://github.com/PyCQA/flake8.git
synced 2026-03-29 10:36:53 +00:00
Merge branch 'feature/207' into 'master'
Add --bug-report flag to help bug reporters When invoked it will print out JSON that has all of the debugging information needed by the maintainers to diagnose or reproduce a bug. Closes #207 See merge request !117
This commit is contained in:
commit
e8cb26895e
7 changed files with 215 additions and 2 deletions
4
docs/source/release-notes/3.1.0.rst
Normal file
4
docs/source/release-notes/3.1.0.rst
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
3.1.0 -- 2016-yy-xx
|
||||
-------------------
|
||||
|
||||
- Add ``--bug-report`` flag to make issue reporters' lives easier.
|
||||
|
|
@ -6,6 +6,7 @@ All of the release notes that have been recorded for Flake8 are organized here
|
|||
with the newest releases first.
|
||||
|
||||
.. toctree::
|
||||
3.1.0
|
||||
3.0.4
|
||||
3.0.3
|
||||
3.0.2
|
||||
|
|
|
|||
|
|
@ -728,3 +728,51 @@
|
|||
flake8 --benchmark dir/
|
||||
|
||||
This **can not** be specified in config files.
|
||||
|
||||
|
||||
.. option:: --bug-report
|
||||
|
||||
Generate information necessary to file a complete bug report for Flake8.
|
||||
This will pretty-print a JSON blob that should be copied and pasted into a
|
||||
bug report for Flake8.
|
||||
|
||||
Command-line usage:
|
||||
|
||||
.. prompt:: bash
|
||||
|
||||
flake8 --bug-report
|
||||
|
||||
The output should look vaguely like:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"dependency": "setuptools",
|
||||
"version": "25.1.1"
|
||||
}
|
||||
],
|
||||
"platform": {
|
||||
"python_implementation": "CPython",
|
||||
"python_version": "2.7.12",
|
||||
"system": "Darwin"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"plugin": "mccabe",
|
||||
"version": "0.5.1"
|
||||
},
|
||||
{
|
||||
"plugin": "pycodestyle",
|
||||
"version": "2.0.0"
|
||||
},
|
||||
{
|
||||
"plugin": "pyflakes",
|
||||
"version": "1.2.3"
|
||||
}
|
||||
],
|
||||
"version": "3.1.0.dev0"
|
||||
}
|
||||
|
||||
This **can not** be specified in config files.
|
||||
|
|
|
|||
62
src/flake8/main/debug.py
Normal file
62
src/flake8/main/debug.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
"""Module containing the logic for our debugging logic."""
|
||||
from __future__ import print_function
|
||||
|
||||
import json
|
||||
import platform
|
||||
|
||||
import setuptools
|
||||
|
||||
|
||||
def print_information(option, option_string, value, parser,
|
||||
option_manager=None):
|
||||
"""Print debugging information used in bug reports.
|
||||
|
||||
: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:
|
||||
# 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)
|
||||
|
||||
|
||||
def information(option_manager):
|
||||
"""Generate the information to be printed for the bug report."""
|
||||
return {
|
||||
'version': option_manager.version,
|
||||
'plugins': plugins_from(option_manager),
|
||||
'dependencies': dependencies(),
|
||||
'platform': {
|
||||
'python_implementation': platform.python_implementation(),
|
||||
'python_version': platform.python_version(),
|
||||
'system': platform.system(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def plugins_from(option_manager):
|
||||
"""Generate the list of plugins installed."""
|
||||
return [{'plugin': plugin, 'version': version}
|
||||
for (plugin, version) in sorted(option_manager.registered_plugins)]
|
||||
|
||||
|
||||
def dependencies():
|
||||
"""Generate the list of dependencies we care about."""
|
||||
return [{'dependency': 'setuptools', 'version': setuptools.__version__}]
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"""Contains the logic for all of the default options for Flake8."""
|
||||
from flake8 import defaults
|
||||
from flake8.main import debug
|
||||
from flake8.main import vcs
|
||||
|
||||
|
||||
|
|
@ -29,6 +30,8 @@ def register_default_options(option_manager):
|
|||
- ``--append-config``
|
||||
- ``--config``
|
||||
- ``--isolated``
|
||||
- ``--benchmark``
|
||||
- ``--bug-report``
|
||||
"""
|
||||
add_option = option_manager.add_option
|
||||
|
||||
|
|
@ -199,3 +202,11 @@ def register_default_options(option_manager):
|
|||
'--benchmark', default=False, action='store_true',
|
||||
help='Print benchmark information about this run of Flake8',
|
||||
)
|
||||
|
||||
# Debugging
|
||||
|
||||
add_option(
|
||||
'--bug-report', action='callback', callback=debug.print_information,
|
||||
callback_kwargs={'option_manager': option_manager},
|
||||
help='Print information necessary when preparing a bug report',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -240,9 +240,10 @@ class OptionManager(object):
|
|||
LOG.debug('Extending default select list with %r', error_codes)
|
||||
self.extended_default_select.update(error_codes)
|
||||
|
||||
def generate_versions(self, format_str='%(name)s: %(version)s'):
|
||||
def generate_versions(self, format_str='%(name)s: %(version)s',
|
||||
join_on=', '):
|
||||
"""Generate a comma-separated list of versions of plugins."""
|
||||
return ', '.join(
|
||||
return join_on.join(
|
||||
format_str % self.format_plugin(plugin)
|
||||
for plugin in self.registered_plugins
|
||||
)
|
||||
|
|
|
|||
86
tests/unit/test_debug.py
Normal file
86
tests/unit/test_debug.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
"""Tests for our debugging module."""
|
||||
import mock
|
||||
import pytest
|
||||
import setuptools
|
||||
|
||||
from flake8.main import debug
|
||||
|
||||
|
||||
def test_dependencies():
|
||||
"""Verify that we format our dependencies appropriately."""
|
||||
expected = [{'dependency': 'setuptools',
|
||||
'version': setuptools.__version__}]
|
||||
assert expected == debug.dependencies()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('plugins, expected', [
|
||||
([], []),
|
||||
([('pycodestyle', '2.0.0')], [{'plugin': 'pycodestyle',
|
||||
'version': '2.0.0'}]),
|
||||
([('pycodestyle', '2.0.0'), ('mccabe', '0.5.9')],
|
||||
[{'plugin': 'mccabe', 'version': '0.5.9'},
|
||||
{'plugin': 'pycodestyle', 'version': '2.0.0'}]),
|
||||
])
|
||||
def test_plugins_from(plugins, expected):
|
||||
"""Test that we format plugins appropriately."""
|
||||
option_manager = mock.Mock(registered_plugins=set(plugins))
|
||||
assert expected == debug.plugins_from(option_manager)
|
||||
|
||||
|
||||
@mock.patch('platform.python_implementation', return_value='CPython')
|
||||
@mock.patch('platform.python_version', return_value='3.5.3')
|
||||
@mock.patch('platform.system', return_value='Linux')
|
||||
def test_information(system, pyversion, pyimpl):
|
||||
"""Verify that we return all the information we care about."""
|
||||
expected = {
|
||||
'version': '3.1.0',
|
||||
'plugins': [{'plugin': 'mccabe', 'version': '0.5.9'},
|
||||
{'plugin': 'pycodestyle', 'version': '2.0.0'}],
|
||||
'dependencies': [{'dependency': 'setuptools',
|
||||
'version': setuptools.__version__}],
|
||||
'platform': {
|
||||
'python_implementation': 'CPython',
|
||||
'python_version': '3.5.3',
|
||||
'system': 'Linux',
|
||||
},
|
||||
}
|
||||
option_manager = mock.Mock(
|
||||
registered_plugins=set([('pycodestyle', '2.0.0'),
|
||||
('mccabe', '0.5.9')]),
|
||||
version='3.1.0',
|
||||
)
|
||||
assert expected == debug.information(option_manager)
|
||||
pyimpl.assert_called_once_with()
|
||||
pyversion.assert_called_once_with()
|
||||
system.assert_called_once_with()
|
||||
|
||||
|
||||
@mock.patch('flake8.main.debug.print')
|
||||
@mock.patch('flake8.main.debug.information', return_value={})
|
||||
@mock.patch('json.dumps', return_value='{}')
|
||||
def test_print_information_no_plugins(dumps, information, print_mock):
|
||||
"""Verify we print and exit only when we have plugins."""
|
||||
plugins = []
|
||||
option_manager = mock.Mock(registered_plugins=set(plugins))
|
||||
assert debug.print_information(
|
||||
None, None, None, None, option_manager=option_manager,
|
||||
) is None
|
||||
assert dumps.called is False
|
||||
assert information.called is False
|
||||
assert print_mock.called is False
|
||||
|
||||
|
||||
@mock.patch('flake8.main.debug.print')
|
||||
@mock.patch('flake8.main.debug.information', return_value={})
|
||||
@mock.patch('json.dumps', return_value='{}')
|
||||
def test_print_information(dumps, information, print_mock):
|
||||
"""Verify we print and exit only when we have plugins."""
|
||||
plugins = [('pycodestyle', '2.0.0'), ('mccabe', '0.5.9')]
|
||||
option_manager = mock.Mock(registered_plugins=set(plugins))
|
||||
with pytest.raises(SystemExit):
|
||||
debug.print_information(
|
||||
None, None, None, None, option_manager=option_manager,
|
||||
)
|
||||
print_mock.assert_called_once_with('{}')
|
||||
dumps.assert_called_once_with({}, indent=2, sort_keys=True)
|
||||
information.assert_called_once_with(option_manager)
|
||||
Loading…
Add table
Add a link
Reference in a new issue