diff --git a/MANIFEST.in b/MANIFEST.in index b87aff1..96b10e7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,8 @@ include *.rst include CONTRIBUTORS.txt include LICENSE -recursive-include docs * -recursive-include tests * -recursive-include src * +global-exclude *.pyc +recursive-include docs *.rst *.py +recursive-include tests *.py *.ini *.rst *_diff +recursive-include src *.py +prune docs/build/ diff --git a/setup.py b/setup.py index 2624db4..cd002cd 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ -"""Packaging logic for Flake8.""" # -*- coding: utf-8 -*- -from __future__ import with_statement - +"""Packaging logic for Flake8.""" import functools import io import os @@ -13,25 +11,8 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) import flake8 # noqa -try: - # Work around a traceback with Nose on Python 2.6 - # http://bugs.python.org/issue15881#msg170215 - __import__('multiprocessing') -except ImportError: - pass - -try: - # Use https://docs.python.org/3/library/unittest.mock.html - from unittest import mock -except ImportError: - # < Python 3.3 - mock = None - - -tests_require = ['pytest'] -if mock is None: - tests_require.append('mock') +tests_require = ['mock', 'pytest'] requires = [ "pyflakes >= 0.8.1, != 1.2.0, != 1.2.1, != 1.2.2, < 1.3.0", @@ -47,7 +28,7 @@ if sys.version_info < (3, 2): def get_long_description(): - """Generate a long description from the README and CHANGES files.""" + """Generate a long description from the README file.""" descr = [] for fname in ('README.rst',): with io.open(fname, encoding='utf-8') as f: @@ -130,6 +111,7 @@ setuptools.setup( }, classifiers=[ "Environment :: Console", + "Framework :: Flake8", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", diff --git a/src/flake8/api/__init__.py b/src/flake8/api/__init__.py index 9f95557..c2eefbe 100644 --- a/src/flake8/api/__init__.py +++ b/src/flake8/api/__init__.py @@ -3,8 +3,3 @@ This is the only submodule in Flake8 with a guaranteed stable API. All other submodules are considered internal only and are subject to change. """ - - -def get_style_guide(**kwargs): - """Stub out the only function I'm aware of people using.""" - pass diff --git a/src/flake8/api/legacy.py b/src/flake8/api/legacy.py new file mode 100644 index 0000000..35c7101 --- /dev/null +++ b/src/flake8/api/legacy.py @@ -0,0 +1,152 @@ +"""Module containing shims around Flake8 2.0 behaviour.""" +import logging +import os.path + +from flake8.formatting import base as formatter +from flake8.main import application as app + +LOG = logging.getLogger(__name__) + + +def get_style_guide(**kwargs): + """Stub out the only function I'm aware of people using.""" + application = app.Application() + application.initialize([]) + options = application.options + for key, value in kwargs.items(): + try: + getattr(options, key) + setattr(options, key, value) + except AttributeError: + LOG.error('Could not update option "%s"', key) + return StyleGuide(application) + + +class StyleGuide(object): + """Public facing object that mimic's Flake8 2.0's StyleGuide. + + .. note:: + + There are important changes in how this object behaves compared to + the StyleGuide object provided in Flake8 2.x. + + .. warning:: + + This object should not be instantiated directly by users. + + .. versionchanged:: 3.0.0 + """ + + def __init__(self, application): + """Initialize our StyleGuide.""" + self._application = application + self._file_checker_manager = application.file_checker_manager + + @property + def options(self): + """The parsed options. + + An instance of :class:`optparse.Values` containing parsed options. + """ + return self._application.options + + @property + def paths(self): + """The extra arguments passed as paths.""" + return self._application.paths + + def check_files(self, paths=None): + """Run collected checks on the files provided. + + This will check the files passed in and return a :class:`Report` + instance. + + :param list paths: + List of filenames (or paths) to check. + :returns: + Object that mimic's Flake8 2.0's Reporter class. + :rtype: + flake8.api.legacy.Report + """ + self._application.run_checks(paths) + self._application.report_errors() + return Report(self._application) + + def excluded(self, filename, parent=None): + """Determine if a file is excluded. + + :param str filename: + Path to the file to check if it is excluded. + :param str parent: + Name of the parent directory containing the file. + :returns: + True if the filename is excluded, False otherwise. + :rtype: + bool + """ + return (self._file_checker_manager.is_path_excluded(filename) or + (parent and + self._file_checker_manager.is_path_excluded( + os.path.join(parent, filename)))) + + def init_report(self, reporter=None): + """Set up a formatter for this run of Flake8.""" + if (reporter is not None and + not issubclass(reporter, formatter.BaseFormatter)): + raise ValueError("Report should be subclass of " + "flake8.formatter.BaseFormatter.") + self._application.make_formatter(reporter) + self._application.guide = None + # NOTE(sigmavirus24): This isn't the intended use of + # Application#make_guide but it works pretty well. + # Stop cringing... I know it's gross. + self._application.make_guide() + + def input_file(self, filename, lines=None, expected=None, line_offset=0): + """Run collected checks on a single file. + + This will check the file passed in and return a :class:`Report` + instance. + + :param str filename: + The path to the file to check. + :param list lines: + Ignored since Flake8 3.0. + :param expected: + Ignored since Flake8 3.0. + :param int line_offset: + Ignored since Flake8 3.0. + :returns: + Object that mimic's Flake8 2.0's Reporter class. + :rtype: + flake8.api.legacy.Report + """ + return self.check_files([filename]) + + +class Report(object): + """Public facing object that mimic's Flake8 2.0's API. + + .. note:: + + There are important changes in how this object behaves compared to + the object provided in Flake8 2.x. + + .. versionchanged:: 3.0.0 + """ + + def __init__(self, application): + """Initialize the Report for the user. + + .. warning:: This should not be instantiated by users. + """ + self._application = application + + @property + def total_errors(self): + """The total number of errors found by Flake8.""" + return self._application.result_count + + def get_statistics(self, violation): + """Get the number of occurences of a violation.""" + raise NotImplementedError('Statistics capturing needs to happen first') diff --git a/src/flake8/defaults.py b/src/flake8/defaults.py index d9f5a0b..ede2946 100644 --- a/src/flake8/defaults.py +++ b/src/flake8/defaults.py @@ -1,8 +1,8 @@ """Constants that define defaults.""" -EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox' +EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg' IGNORE = 'E121,E123,E126,E226,E24,E704,W503,W504' -SELECT = 'E,F,W,C' +SELECT = 'E,F,W,C90' MAX_LINE_LENGTH = 79 TRUTHY_VALUES = set(['true', '1', 't']) diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py index b1e0772..35feede 100644 --- a/src/flake8/main/application.py +++ b/src/flake8/main/application.py @@ -177,13 +177,15 @@ class Application(object): self.options, self.args) - def make_formatter(self): + def make_formatter(self, formatter_class=None): # type: () -> NoneType """Initialize a formatter based on the parsed options.""" if self.formatter is None: - self.formatter = self.formatting_plugins.get( - self.options.format, self.formatting_plugins['default'] - ).execute(self.options) + if formatter_class is None: + formatter_class = self.formatting_plugins.get( + self.options.format, self.formatting_plugins['default'] + ).execute + self.formatter = formatter_class(self.options) def make_notifier(self): # type: () -> NoneType @@ -212,15 +214,17 @@ class Application(object): checker_plugins=self.check_plugins, ) - def run_checks(self): - # type: () -> NoneType + def run_checks(self, files=None): + # type: (Union[List[str], NoneType]) -> NoneType """Run the actual checks with the FileChecker Manager. This method encapsulates the logic to make a :class:`~flake8.checker.Manger` instance run the checks it is managing. + + :param list files: + List of filenames to process """ - files = None if self.running_against_diff: files = list(sorted(self.parsed_diff.keys())) self.file_checker_manager.start(files) diff --git a/tox.ini b/tox.ini index 940806b..42dac04 100644 --- a/tox.ini +++ b/tox.ini @@ -28,9 +28,7 @@ commands = # Linters [testenv:flake8] -skipsdist = true skip_install = true -use_develop = false deps = flake8 flake8-docstrings>=0.2.7 @@ -40,9 +38,7 @@ commands = [testenv:pylint] basepython = python3 -skipsdist = true skip_install = true -use_develop = false deps = pyflakes pylint @@ -51,9 +47,7 @@ commands = [testenv:doc8] basepython = python3 -skipsdist = true skip_install = true -use_develop = false deps = sphinx doc8 @@ -62,9 +56,7 @@ commands = [testenv:mypy] basepython = python3 -skipsdist = true skip_install = true -use_develop = false deps = mypy-lang commands = @@ -72,9 +64,7 @@ commands = [testenv:bandit] basepython = python3 -skipsdist = true skip_install = true -use_develop = false deps = bandit commands = @@ -82,9 +72,7 @@ commands = [testenv:linters] basepython = python3 -skipsdist = true skip_install = true -use_develop = false deps = {[testenv:flake8]deps} {[testenv:pylint]deps} @@ -108,9 +96,7 @@ commands = [testenv:serve-docs] basepython = python3 -skipsdist = true skip_install = true -use_develop = false changedir = docs/build/html deps = commands = @@ -124,10 +110,8 @@ commands = python setup.py check -r -s [testenv:release] -skipsdist = true basepython = python3 skip_install = true -use_develop = false deps = wheel setuptools