Mercurial hook broken in multiple ways
Summary: "pip --install-hook mercurial" is broken, and also the hook function doesn't work
Please describe how you installed Flake8
I originally installed flake8 into my Python virtual environment using pip install -r dev-requirements.txt
where dev-requirements.txt
has flake8
in it.
Later, as I tried to debug and fix the mercurial hook, I cloned the master branch of flake8 and did a "pip install -e ." from the flake8 repo directory. I did this using pip from a virtual environment in which I had installed mercurial, for reasons I explain below. The flake8 --bug-report
output below is from that latter installation.
Please provide the exact, unmodified output of flake8 --bug-report
{
"dependencies": [
{
"dependency": "setuptools",
"version": "31.0.0"
}
],
"platform": {
"python_implementation": "CPython",
"python_version": "2.7.12",
"system": "Linux"
},
"plugins": [
{
"plugin": "mccabe",
"version": "0.5.2"
},
{
"plugin": "pycodestyle",
"version": "2.2.0"
},
{
"plugin": "pyflakes",
"version": "1.3.0"
}
],
"version": "3.3.0.dev0"
}
Please describe the problem or feature
If this is a bug report, please explain with examples (and example code) what you expected to happen and what actually happened.
This command failed :
$ flake8 --install-hook mercurial
Could not find the mercurial directory
I located the cause of this error in flake8/main/mercurial.py . The find_hgrc()
function runs hg root
and grabs the output to use as the directory name, but it neglects to strip the trailing newline from the output text, so the directory cannot be found.
I fixed that problem and re-ran flake8 --install-hook mercurial
. I got a new error:
TypeError: option values must be strings
That error was due to this line in the install()
function:
hgconfig.set('flake8', 'strict', False)
I changed it to this:
hgconfig.set('flake8', 'strict', 'false')
I also took the opportunity to fix another error in configparser usage, in this line in the hook()
function:
strict = hgconfig.get('flake8', 'strict', fallback=True)
That line needed to be changed to become this:
strict = hgconfig.getboolean('flake8', 'strict', fallback=True)
After I made these changes, finally the flake8 --install-hook mercurial
command completed without errors and installed the hook function in the .hg/hgrc file. But my problems were not over.
On committing, I got this message:
abort: commit hook is invalid: import of "flake8.main.mercurial" failed
(run with --traceback for stack trace)
The problem here is that I installed flake8 into my project's virtual environment, like I normally do, but mercurial is installed globally (like it normally would be.) Because the commit hook is a Python function, mercurial is trying to import it from mercurial's process, but mercurial can't find it because flake8 has been installed into a virtual environment -- effectively a whole different Python installation. This issue is compounded by the fact that I use Python 3 for all my new projects, but mercurial only runs on Python 2, so flake8 installed for Python 3 will never be accessible to it.
At this point I became more stubborn and tried to see if there was some way I could make the mercurlal hook work. After all, it was documented! It must have worked at some point in time. So I resumed testing with a Python 2 project created for this purpose, and installed flake8 into the same virtual environment in which mercurial was installed. I would never do this in normal development, it was just for the purposes of this bug report.
Doing all of those things I finally got the flake8 mercurial hook function to run, and it did indeed run checks on the code in my test project. But even when I set strict = true
it would never stop the commit from happening, no matter how many errors flake8 found.
After some more research, I found out that the flake8 mercurial hook is registered with the wrong event type. It is registered as a "commit" hook, but the commit hook doesn't run until after the commit is complete. It has no ability to stop a commit from happening, it is only for the purpose of sending emails reporting commits, and such. The correct hook event to use is not "commit", but rather "pretxncommit". This runs after the commit files have been written to disk, but not yet finalized. A pretxncommit hook can query what all the changed files are, what the differences are, what the commit ID will be, etc. but if the hook returns non-zero status, the entire transaction is rolled back as if it never happened. This is what you want.
For more details, see the Mercurial hook docs at: http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html
At this point I am of the opinion that flake8/main/mercurial.py needs to be rewritten, not patched. I recommend that we add an if __name__ == '__main__'
clause so it can be run with "python -m flake8.main.mercurial" and set up as a script hook and not a Python function hook. And other fixes are needed too.
I'm making no promises, but if I make all those changes myself and get it working, I'll try and submit a pull request. In the mean time you might consider updating the docs to reflect the fact that mercurial hooks are not supported, so as to avoid someone else having to go through what I just did. (I use both git and mercurial every day, FYI.)