diff options
| author | Anthony Sottile <asottile@umich.edu> | 2021-03-29 19:01:59 -0700 |
|---|---|---|
| committer | Anthony Sottile <asottile@umich.edu> | 2021-03-29 19:01:59 -0700 |
| commit | 1a1d850e99bb28625e47f4aa3c27014a3ac6758a (patch) | |
| tree | 8f8c96385848281021783ccba3821f4bf9a645f5 | |
| parent | 434c108f7491b589362752b12979741598bb2dff (diff) | |
| download | flake8-1a1d850e99bb28625e47f4aa3c27014a3ac6758a.tar.gz | |
remove vcs integration
| -rw-r--r-- | docs/source/internal/writing-code.rst | 1 | ||||
| -rw-r--r-- | docs/source/manpage.rst | 3 | ||||
| -rw-r--r-- | docs/source/user/options.rst | 24 | ||||
| -rw-r--r-- | docs/source/user/using-hooks.rst | 76 | ||||
| -rw-r--r-- | src/flake8/checker.py | 7 | ||||
| -rw-r--r-- | src/flake8/exceptions.py | 48 | ||||
| -rw-r--r-- | src/flake8/main/application.py | 2 | ||||
| -rw-r--r-- | src/flake8/main/git.py | 262 | ||||
| -rw-r--r-- | src/flake8/main/mercurial.py | 145 | ||||
| -rw-r--r-- | src/flake8/main/options.py | 9 | ||||
| -rw-r--r-- | src/flake8/main/vcs.py | 48 | ||||
| -rw-r--r-- | src/flake8/plugins/pyflakes.py | 7 | ||||
| -rw-r--r-- | tests/unit/test_git.py | 29 |
13 files changed, 2 insertions, 659 deletions
diff --git a/docs/source/internal/writing-code.rst b/docs/source/internal/writing-code.rst index 9d260b7..d8c63fe 100644 --- a/docs/source/internal/writing-code.rst +++ b/docs/source/internal/writing-code.rst @@ -59,7 +59,6 @@ accepts as well as what it returns. filepaths = list(copy_indexed_files_to(tempdir, lazy)) app.initialize(['.']) app.options.exclude = update_excludes(app.options.exclude, tempdir) - app.options._running_from_vcs = True app.run_checks(filepaths) app.report_errors() diff --git a/docs/source/manpage.rst b/docs/source/manpage.rst index 9c002b9..826ec8b 100644 --- a/docs/source/manpage.rst +++ b/docs/source/manpage.rst @@ -70,9 +70,6 @@ All options available as of Flake8 3.1.0:: Enable plugins and extensions that are otherwise disabled by default --exit-zero Exit with status code "0" even if there are errors. - --install-hook=INSTALL_HOOK - Install a hook that is run prior to a commit for the - supported version control system. -j JOBS, --jobs=JOBS 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 diff --git a/docs/source/user/options.rst b/docs/source/user/options.rst index d2c0edb..885bb02 100644 --- a/docs/source/user/options.rst +++ b/docs/source/user/options.rst @@ -78,8 +78,6 @@ Index of Options - :option:`flake8 --exit-zero` -- :option:`flake8 --install-hook` - - :option:`flake8 --jobs` - :option:`flake8 --output-file` @@ -751,28 +749,6 @@ Options and their Descriptions This **can not** be specified in config files. -.. option:: --install-hook=VERSION_CONTROL_SYSTEM - - :ref:`Go back to index <top>` - - Install a hook for your version control system that is executed before - or during commit. - - The available options are: - - - git - - mercurial - - Command-line usage: - - .. prompt:: bash - - flake8 --install-hook=git - flake8 --install-hook=mercurial - - This **can not** be specified in config files. - - .. option:: --jobs=<n> :ref:`Go back to index <top>` diff --git a/docs/source/user/using-hooks.rst b/docs/source/user/using-hooks.rst index d156ad0..d8af5f8 100644 --- a/docs/source/user/using-hooks.rst +++ b/docs/source/user/using-hooks.rst @@ -36,83 +36,7 @@ plugins, use the ``additional_dependencies`` setting. - id: flake8 additional_dependencies: [flake8-docstrings] - -Built-in Hook Integration -========================= - -.. note:: - - It is strongly suggested to use |Flake8| via `pre-commit`_ over the - built-in hook mechanisms. ``pre-commit`` smooths out many of the rough - edges of ``git`` and is much more battle-tested than the |Flake8| - hook implementation. - -|Flake8| can be integrated into your development workflow in many ways. A -default installation of |Flake8| can install pre-commit hooks for both -`Git`_ and `Mercurial`_. To install a built-in hook, you can use the -:option:`flake8 --install-hook` command-line option. For example, you can -install a git pre-commit hook by running: - -.. prompt:: bash - - flake8 --install-hook git - -This will install the pre-commit hook into ``.git/hooks/``. Alternatively, -you can install the mercurial commit hook by running - -.. prompt:: bash - - flake8 --install-hook mercurial - - -Preventing Commits -================== - -By default, |Flake8| does not prevent you from creating a commit with these -hooks. Both hooks can be configured to be strict easily. - -Both our Git and Mercurial hooks check for the presence of ``flake8.strict`` -in each VCS' config. For example, you might configure this like so: - -.. prompt:: bash - - git config --bool flake8.strict true - hg config flake8.strict true - - -Checking All Modified Files Currently Tracked -============================================= - -.. note:: - - Mercurial does not have the concept of an index or "stage" as best as I - understand. - -|Flake8| aims to make smart choices that keep things fast for users where -possible. As a result, the |Flake8| Git pre-commit will default to only -checking files that have been staged (i.e., added to the index). If, however, -you are keen to be lazy and not independently add files to your git index, you -can set ``flake8.lazy`` to ``true`` (similar to how you would set -``flake8.strict`` above) and this will check all tracked files. - -This is to support users who often find themselves doing things like: - -.. prompt:: bash - - git commit -a - -.. note:: - - If you have files you have not yet added to the index, |Flake8| will not - see these and will not check them for you. You must ``git-add`` them - first. - - .. _pre-commit: https://pre-commit.com/ .. _pre-commit docs: https://pre-commit.com/#pre-commit-configyaml---hooks -.. _Git: - https://git-scm.com/ -.. _Mercurial: - https://www.mercurial-scm.org/ diff --git a/src/flake8/checker.py b/src/flake8/checker.py index b4ef97d..224bf64 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -200,7 +200,6 @@ class Manager(object): paths = ["."] filename_patterns = self.options.filename - running_from_vcs = self.options._running_from_vcs running_from_diff = self.options.diff # NOTE(sigmavirus24): Yes this is a little unsightly, but it's our @@ -218,10 +217,8 @@ class Manager(object): # the event that the argument and the filename are identical. # If it was specified explicitly, the user intended for it to be # checked. - explicitly_provided = ( - not running_from_vcs - and not running_from_diff - and (argument == filename) + explicitly_provided = not running_from_diff and ( + argument == filename ) return ( explicitly_provided or matches_filename_patterns diff --git a/src/flake8/exceptions.py b/src/flake8/exceptions.py index bef6f4b..ea9bc98 100644 --- a/src/flake8/exceptions.py +++ b/src/flake8/exceptions.py @@ -93,51 +93,3 @@ class PluginExecutionFailed(Flake8Exception): "name": self.plugin["plugin_name"], "exc": self.original_exception, } - - -class HookInstallationError(Flake8Exception): - """Parent exception for all hooks errors.""" - - -class GitHookAlreadyExists(HookInstallationError): - """Exception raised when the git pre-commit hook file already exists.""" - - def __init__(self, path): # type: (str) -> None - """Initialize the exception message from the `path`.""" - self.path = path - tmpl = ( - "The Git pre-commit hook ({0}) already exists. To convince " - "Flake8 to install the hook, please remove the existing " - "hook." - ) - super(GitHookAlreadyExists, self).__init__(tmpl.format(self.path)) - - -class MercurialHookAlreadyExists(HookInstallationError): - """Exception raised when a mercurial hook is already configured.""" - - hook_name = None # type: str - - def __init__(self, path, value): # type: (str, str) -> None - """Initialize the relevant attributes.""" - self.path = path - self.value = value - tmpl = ( - 'The Mercurial {0} hook already exists with "{1}" in {2}. ' - "To convince Flake8 to install the hook, please remove the " - "{0} configuration from the [hooks] section of your hgrc." - ) - msg = tmpl.format(self.hook_name, self.value, self.path) - super(MercurialHookAlreadyExists, self).__init__(msg) - - -class MercurialCommitHookAlreadyExists(MercurialHookAlreadyExists): - """Exception raised when the hg commit hook is already configured.""" - - hook_name = "commit" - - -class MercurialQRefreshHookAlreadyExists(MercurialHookAlreadyExists): - """Exception raised when the hg commit hook is already configured.""" - - hook_name = "qrefresh" diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py index 5c9e076..a247062 100644 --- a/src/flake8/main/application.py +++ b/src/flake8/main/application.py @@ -191,8 +191,6 @@ class Application(object): if not self.parsed_diff: self.exit() - self.options._running_from_vcs = False - self.check_plugins.provide_options( self.option_manager, self.options, self.args ) diff --git a/src/flake8/main/git.py b/src/flake8/main/git.py deleted file mode 100644 index 683396a..0000000 --- a/src/flake8/main/git.py +++ /dev/null @@ -1,262 +0,0 @@ -"""Module containing the main git hook interface and helpers. - -.. autofunction:: hook -.. autofunction:: install - -""" -import contextlib -import os -import os.path -import shutil -import stat -import subprocess -import sys -import tempfile - -from flake8 import defaults -from flake8 import exceptions - -__all__ = ("hook", "install") - - -def hook(lazy=False, strict=False): - """Execute Flake8 on the files in git's index. - - Determine which files are about to be committed and run Flake8 over them - to check for violations. - - :param bool lazy: - Find files not added to the index prior to committing. This is useful - if you frequently use ``git commit -a`` for example. This defaults to - False since it will otherwise include files not in the index. - :param bool strict: - If True, return the total number of errors/violations found by Flake8. - This will cause the hook to fail. - :returns: - Total number of errors found during the run. - :rtype: - int - """ - # NOTE(sigmavirus24): Delay import of application until we need it. - from flake8.main import application - - app = application.Application() - with make_temporary_directory() as tempdir: - filepaths = list(copy_indexed_files_to(tempdir, lazy)) - app.initialize(["."]) - app.options.exclude = update_excludes(app.options.exclude, tempdir) - app.options._running_from_vcs = True - # Apparently there are times when there are no files to check (e.g., - # when amending a commit). In those cases, let's not try to run checks - # against nothing. - if filepaths: - app.run_checks(filepaths) - - # If there were files to check, update their paths and report the errors - if filepaths: - update_paths(app.file_checker_manager, tempdir) - app.report_errors() - - if strict: - return app.result_count - return 0 - - -def install(): - """Install the git hook script. - - This searches for the ``.git`` directory and will install an executable - pre-commit python script in the hooks sub-directory if one does not - already exist. - - It will also print a message to stdout about how to configure the hook. - - :returns: - True if successful, False if the git directory doesn't exist. - :rtype: - bool - :raises: - flake8.exceptions.GitHookAlreadyExists - """ - git_directory = find_git_directory() - if git_directory is None or not os.path.exists(git_directory): - return False - - hooks_directory = os.path.join(git_directory, "hooks") - if not os.path.exists(hooks_directory): - os.mkdir(hooks_directory) - - pre_commit_file = os.path.abspath( - os.path.join(hooks_directory, "pre-commit") - ) - if os.path.exists(pre_commit_file): - raise exceptions.GitHookAlreadyExists(path=pre_commit_file) - - executable = get_executable() - - with open(pre_commit_file, "w") as fd: - fd.write(_HOOK_TEMPLATE.format(executable=executable)) - - # NOTE(sigmavirus24): The following sets: - # - read, write, and execute permissions for the owner - # - read permissions for people in the group - # - read permissions for other people - # The owner needs the file to be readable, writable, and executable - # so that git can actually execute it as a hook. - pre_commit_permissions = stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH - os.chmod(pre_commit_file, pre_commit_permissions) - - print("git pre-commit hook installed, for configuration options see") - print("http://flake8.pycqa.org/en/latest/user/using-hooks.html") - - return True - - -def get_executable(): - if sys.executable is not None: - return sys.executable - return "/usr/bin/env python" - - -def find_git_directory(): - rev_parse = piped_process(["git", "rev-parse", "--git-dir"]) - - (stdout, _) = rev_parse.communicate() - stdout = to_text(stdout) - - if rev_parse.returncode == 0: - return stdout.strip() - return None - - -def copy_indexed_files_to(temporary_directory, lazy): - # some plugins (e.g. flake8-isort) need these files to run their checks - setup_cfgs = find_setup_cfgs(lazy) - for filename in setup_cfgs: - contents = get_staged_contents_from(filename) - copy_file_to(temporary_directory, filename, contents) - - modified_files = find_modified_files(lazy) - for filename in modified_files: - contents = get_staged_contents_from(filename) - yield copy_file_to(temporary_directory, filename, contents) - - -def copy_file_to(destination_directory, filepath, contents): - directory, filename = os.path.split(os.path.abspath(filepath)) - temporary_directory = make_temporary_directory_from( - destination_directory, directory - ) - if not os.path.exists(temporary_directory): - os.makedirs(temporary_directory) - temporary_filepath = os.path.join(temporary_directory, filename) - with open(temporary_filepath, "wb") as fd: - fd.write(contents) - return temporary_filepath - - -def make_temporary_directory_from(destination, directory): - prefix = os.path.commonprefix([directory, destination]) - common_directory_path = os.path.relpath(directory, start=prefix) - return os.path.join(destination, common_directory_path) - - -def find_modified_files(lazy): - diff_index_cmd = [ - "git", - "diff-index", - "--cached", - "--name-only", - "--diff-filter=ACMRTUXB", - "HEAD", - ] - if lazy: - diff_index_cmd.remove("--cached") - - diff_index = piped_process(diff_index_cmd) - (stdout, _) = diff_index.communicate() - stdout = to_text(stdout) - return stdout.splitlines() - - -def find_setup_cfgs(lazy): - setup_cfg_cmd = ["git", "ls-files", "--cached", "*setup.cfg"] - if lazy: - setup_cfg_cmd.remove("--cached") - extra_files = piped_process(setup_cfg_cmd) - (stdout, _) = extra_files.communicate() - stdout = to_text(stdout) - return stdout.splitlines() - - -def get_staged_contents_from(filename): - git_show = piped_process(["git", "show", ":{0}".format(filename)]) - (stdout, _) = git_show.communicate() - return stdout - - -@contextlib.contextmanager -def make_temporary_directory(): - temporary_directory = tempfile.mkdtemp() - yield temporary_directory - shutil.rmtree(temporary_directory, ignore_errors=True) - - -def to_text(string): - """Ensure that the string is text.""" - if callable(getattr(string, "decode", None)): - return string.decode("utf-8") - return string - - -def piped_process(command): - return subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - - -def git_config_for(parameter): - config = piped_process(["git", "config", "--get", "--bool", parameter]) - (stdout, _) = config.communicate() - return to_text(stdout).strip() - - -def config_for(parameter): - environment_variable = "flake8_{0}".format(parameter).upper() - git_variable = "flake8.{0}".format(parameter) - value = os.environ.get(environment_variable, git_config_for(git_variable)) - return value.lower() in defaults.TRUTHY_VALUES - - -def update_excludes(exclude_list, temporary_directory_path): - return [ - (temporary_directory_path + pattern) - if os.path.isabs(pattern) - else pattern - for pattern in exclude_list - ] - - -def update_paths(checker_manager, temp_prefix): - temp_prefix_length = len(temp_prefix) - for checker in checker_manager.checkers: - filename = checker.display_name - if filename.startswith(temp_prefix): - checker.display_name = os.path.relpath( - filename[temp_prefix_length:] - ) - - -_HOOK_TEMPLATE = """#!{executable} -import sys - -from flake8.main import git - -if __name__ == '__main__': - sys.exit( - git.hook( - strict=git.config_for('strict'), - lazy=git.config_for('lazy'), - ) - ) -""" diff --git a/src/flake8/main/mercurial.py b/src/flake8/main/mercurial.py deleted file mode 100644 index c387e84..0000000 --- a/src/flake8/main/mercurial.py +++ /dev/null @@ -1,145 +0,0 @@ -"""Module containing the main mecurial hook interface and helpers. - -.. autofunction:: hook -.. autofunction:: install - -""" -import configparser -import os -import subprocess -from typing import Set - -from flake8 import exceptions as exc - -__all__ = ("hook", "install") - - -def hook(ui, repo, **kwargs): - """Execute Flake8 on the repository provided by Mercurial. - - To understand the parameters read more of the Mercurial documentation - around Hooks: https://www.mercurial-scm.org/wiki/Hook. - - We avoid using the ``ui`` attribute because it can cause issues with - the GPL license that Mercurial is under. We don't import it, but we - avoid using it all the same. - """ - from flake8.main import application - - hgrc = find_hgrc(create_if_missing=False) - if hgrc is None: - print("Cannot locate your root mercurial repository.") - raise SystemExit(True) - - hgconfig = configparser_for(hgrc) - strict = hgconfig.get("flake8", "strict", fallback=True) - - filenames = list(get_filenames_from(repo, kwargs)) - - app = application.Application() - app.initialize(filenames) - app.options._running_from_vcs = True - app.run_checks() - app.report() - - if strict: - return app.result_count - return 0 - - -def install(): - """Ensure that the mercurial hooks are installed. - - This searches for the ``.hg/hgrc`` configuration file and will add commit - and qrefresh hooks to it, if they do not already exist. - - It will also print a message to stdout about how to configure the hook. - - :returns: - True if successful, False if the ``.hg/hgrc`` file doesn't exist. - :rtype: - bool - :raises: - flake8.exceptions.MercurialCommitHookAlreadyExists - :raises: - flake8.exceptions.MercurialQRefreshHookAlreadyExists - """ - hgrc = find_hgrc(create_if_missing=True) - if hgrc is None: - return False - - hgconfig = configparser_for(hgrc) - - if not hgconfig.has_section("hooks"): - hgconfig.add_section("hooks") - - if hgconfig.has_option("hooks", "commit"): - raise exc.MercurialCommitHookAlreadyExists( - path=hgrc, value=hgconfig.get("hooks", "commit") - ) - - if hgconfig.has_option("hooks", "qrefresh"): - raise exc.MercurialQRefreshHookAlreadyExists( - path=hgrc, value=hgconfig.get("hooks", "qrefresh") - ) - - hgconfig.set("hooks", "commit", "python:flake8.main.mercurial.hook") - hgconfig.set("hooks", "qrefresh", "python:flake8.main.mercurial.hook") - - if not hgconfig.has_section("flake8"): - hgconfig.add_section("flake8") - - if not hgconfig.has_option("flake8", "strict"): - hgconfig.set("flake8", "strict", False) - - with open(hgrc, "w") as fd: - hgconfig.write(fd) - - print("mercurial hooks installed, for configuration options see") - print("http://flake8.pycqa.org/en/latest/user/using-hooks.html") - - return True - - -def get_filenames_from(repository, kwargs): - seen_filenames = set() # type: Set[str] - node = kwargs["node"] - for revision in range(repository[node], len(repository)): - for filename in repository[revision].files(): - full_filename = os.path.join(repository.root, filename) - have_seen_filename = full_filename in seen_filenames - filename_does_not_exist = not os.path.exists(full_filename) - if have_seen_filename or filename_does_not_exist: - continue - - seen_filenames.add(full_filename) - if full_filename.endswith(".py"): - yield full_filename - - -def find_hgrc(create_if_missing=False): - root = subprocess.Popen( - ["hg", "root"], stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - - (hg_directory, _) = root.communicate() - if callable(getattr(hg_directory, "decode", None)): - hg_directory = hg_directory.decode("utf-8") - - if not os.path.isdir(hg_directory): - return None - - hgrc = os.path.abspath(os.path.join(hg_directory, ".hg", "hgrc")) - if not os.path.exists(hgrc): - if create_if_missing: - open(hgrc, "w").close() - else: - return None - - return hgrc - - -def configparser_for(path): - parser = configparser.ConfigParser(interpolation=None) - parser.read(path) - return parser diff --git a/src/flake8/main/options.py b/src/flake8/main/options.py index 0a6ad45..fdde9e1 100644 --- a/src/flake8/main/options.py +++ b/src/flake8/main/options.py @@ -4,7 +4,6 @@ import functools from flake8 import defaults from flake8.main import debug -from flake8.main import vcs def register_preliminary_options(parser): @@ -323,14 +322,6 @@ def register_default_options(option_manager): ) add_option( - "--install-hook", - action=vcs.InstallAction, - choices=vcs.choices(), - help="Install a hook that is run prior to a commit for the supported " - "version control system.", - ) - - add_option( "-j", "--jobs", default="auto", diff --git a/src/flake8/main/vcs.py b/src/flake8/main/vcs.py deleted file mode 100644 index bf15395..0000000 --- a/src/flake8/main/vcs.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Module containing some of the logic for our VCS installation logic.""" -from __future__ import print_function - -import argparse -import sys - -from flake8 import exceptions as exc -from flake8.main import git -from flake8.main import mercurial - - -# NOTE(sigmavirus24): In the future, we may allow for VCS hooks to be defined -# as plugins, e.g., adding a flake8.vcs entry-point. In that case, this -# dictionary should disappear, and this module might contain more code for -# managing those bits (in conjunction with flake8.plugins.manager). -_INSTALLERS = {"git": git.install, "mercurial": mercurial.install} - - -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)) - - print( - "\nWARNING: flake8 vcs hooks integration is deprecated and " - "scheduled for removal in 4.x. For more information, see " - "https://gitlab.com/pycqa/flake8/issues/568", - file=sys.stderr, - ) - - raise SystemExit(not successful and errored) - - -def choices(): - """Return the list of VCS choices.""" - return list(_INSTALLERS) diff --git a/src/flake8/plugins/pyflakes.py b/src/flake8/plugins/pyflakes.py index b9b30d0..e845483 100644 --- a/src/flake8/plugins/pyflakes.py +++ b/src/flake8/plugins/pyflakes.py @@ -2,13 +2,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -try: - # The 'demandimport' breaks pyflakes and flake8.plugins.pyflakes - from mercurial import demandimport -except ImportError: - pass -else: - demandimport.disable() import os from typing import List diff --git a/tests/unit/test_git.py b/tests/unit/test_git.py deleted file mode 100644 index 24d4b5b..0000000 --- a/tests/unit/test_git.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Tests around functionality in the git integration.""" -import mock -import pytest - -from flake8.main import git - - -@pytest.mark.parametrize('lazy', [True, False]) -def test_find_modified_files(lazy): - """Confirm our logic for listing modified files.""" - if lazy: - # Here --cached is missing - call = [ - 'git', 'diff-index', '--name-only', '--diff-filter=ACMRTUXB', - 'HEAD' - ] - else: - call = [ - 'git', 'diff-index', '--cached', '--name-only', - '--diff-filter=ACMRTUXB', 'HEAD' - ] - mocked_popen = mock.Mock() - mocked_popen.communicate.return_value = ('', '') - - with mock.patch('flake8.main.git.piped_process') as piped_process: - piped_process.return_value = mocked_popen - git.find_modified_files(lazy) - - piped_process.assert_called_once_with(call) |
