Repository navigation
Description
I'm trying to get Prospector ( https://github.com/landscapeio/prospector ) to run as a pre-commit hook. By default, Prospector will run pylint, including pylint's import checker.
I was getting confused, because I kept getting output that looks like:
test_mailing_lists (<projectname>/tests/services/platform/email/test_mailing_lists.py):
L8:0 None: pylint - F0401
Unable to import '<projectname>.services.platform.email.mailing_lists'
Even though directly running Prospector in the project (as opposed to via pre-commit) doesn't raise these errors.
When I print sys.path from inside a Python script run by pre-commit as a hook, I get:
['/Users/gkisel/.pre-commit/repodfiUn9/py_env/bin',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python27.zip',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7/plat-darwin',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7/plat-mac',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7/plat-mac/lib-scriptpackages',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/Extras/lib/python',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7/lib-tk',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7/lib-old',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7/lib-dynload',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7/site-packages',
'/Users/gkisel/.pre-commit/repodfiUn9/py_env/lib/python2.7/site-packages/astroid/brain']
Note that this doesn't appear to include my project directory or my virtualenv path. I think this is why pylint (within Prospector) is unable to successfully run those import statements.
Is this behavior intentional? Am I doing something incorrectly? Is there a way I can get pre-commit to preserve my sys.path when running Python hooks?
For reference, see https://github.com/guykisel/prospector-mirror
Activity
asottile commentedon Oct 22, 2014
This is actually by design. The hooks run in their own isolated environments.
A workaround is to use the
systemtype and call the thing by name. Though this generally goes against the isolation mentaility, but might be enough to get this working for you :)guykisel commentedon Oct 22, 2014
I'm sorry, I'm not sure I understand what you mean by
Can you explain this in a little more detail? I appreciate your help.
asottile commentedon Oct 22, 2014
In hooks.yaml you can create a hook which is of the langauge
systeminstead ofpython. This has the unfortunate side-effect of depending on what is installed at the system level and doesn't take advantage of any of the containerization, etc. But it'll execute using the environment (including pythonpath, etc.) of the shell at the time ofgit commit(instead of executing in an isolated virtualenvironment).An example file might look like this:
guykisel commentedon Oct 22, 2014
Oh, I see. Yes, I think that could work. I'll give it a try, thanks!
asottile commentedon Oct 22, 2014
If that doesn't work let me know! We unfortunately haven't had many usecases with those type of hooks so there's potential that they're completely broken for anything beyond the trivial case that is tested: https://github.com/pre-commit/pre-commit/blob/master/testing/resources/system_hook_with_spaces_repo/hooks.yaml
guykisel commentedon Oct 23, 2014
Update on this: looks like prospector can only accept one file/directory at a time as input, so I set my
hooks.yamlto:Without the
-n 1it just fails silently.carlio commentedon Oct 23, 2014
@guykisel Actually prospector only accepting one file/directory at a time is a bug in prospector (or rather, 'lack of feature'). It's something that comes as a consequences of using setoptconf that I haven't looked into yet. Perhaps a better fix for all this is fixing prospector's behaviour?
carlio commentedon Oct 23, 2014
@asottile Does isolating the environment work in general? I have found with prospector that without the complete path, most tools fail at inferring types and a lot of checks are somewhat worthless. I'm mostly talking about pylint here though.
asottile commentedon Oct 23, 2014
@carlio
For the most part it seems to work unless the tool attempts to do in-depth import / type analysis on the dependencies of the code. pylint is the first time we've run into issues like this and it seems a somewhat acceptable workaround to use the
systemtype and allow that. The unfortunate thing is it then depends on system libraries.For pylint in particular, using an isolated version is really only useful for the syntax checks it does and not the import / type checks. Which has some nonzero benefit, but yeah isn't really ideal :/
@guykisel I like the
-n 1hack, I should probably make a way to encorporate that as a supported option. Thoughts?guykisel commentedon Oct 23, 2014
@asottile you could add another field in
hooks.yaml, something likerun_per_file: true/false. Alternatively, create a new "language", likesystem_once_per_file, but then you're out of luck if you want to get the same behavior underpythonand such.It could also be interesting to try to run xargs in parallel to speed up processing of lots of files.
lokesh1729 commentedon Jun 15, 2019
@asottile
I have configured pylint as a system hook, but it wasn't taking the current shell's python virtualenv...
this is the config I have
when I run it, I see the path of
pythonandpylintlike thisBut, when I see
pythonandpylintpaths, they are in virtual env...asottile commentedon Jun 15, 2019
Can you
hash -rand then try yourwhichcommands again?lokesh1729 commentedon Jun 15, 2019
@asottile yes...
hash -rdoes the trick... but I am wondered what's the trick??? what is the cause of the issue??/ I actually have installedpre-commithooks before and thencleanthem and then tried installing again... is that what it's causing... can you explain what's the logic behind ???asottile commentedon Jun 15, 2019
your
whichcommand was misleading you -- the shell implements a "cache" of binaries that it knows fromPATHlookup -- whereaspre-commitcomputes the executable location from the "true"PATHlokesh1729 commentedon Jun 15, 2019
@asottile the reason I added "which" is because
pylintis failing to detectdjangoin my current virtual environment... ifpre-commitcomputes the location, then why does it fail to importdjango??asottile commentedon Jun 15, 2019
right -- I was mostly explaining why in your screenshot above you see a different
whichresult inside / outside ofpre-commit. because thewhichs outside are using your shell'shashcache despite not actually being available.For
language: systemyou should be able to run the same command in and outside of pre-commit and receive the same results -- if you can show that not happening then there's a bug (though a report that involveswhichor shell builtins should runhash -rbefore invoking those commands). Alternatively you can usepython3 -c 'import shutil; print(shutil.which("python"))'etc.an aside, one thing I noticed about the screenshot above is the
whichhooks show up asFailed-- however that seems impossible since which produced a result and exited zero -- was your configuration something different from that?lokesh1729 commentedon Jun 15, 2019
@asottile keeping
whichaside, the main purpose to put them is to debugpylintfailing to import django. my main concern ispylintnot thewhichcommands. so, when I didhash -rnot onlywhichcommand is showing the correct path but also previouspylinterrors also gone, now it's able to importdjangofrom my virtual environment. can you tell me one thing, I am usingpyenvto manage my python versions. sincepyenvmanipulatesPATHvariable by appending it'sshimspath in the front, does it affectingpylintpath ???asottile commentedon Jun 15, 2019
pyenvitself shouldn't matter, but I imagine the hash cache (while in your shell) also was causing you to use thepylintexecutable you didn't expect as well?lokesh1729 commentedon Jun 15, 2019
I didn't try with my shell before clearing the
hash, so should I put ahash -rbefore the command?? is that needed for thesystemcommands ???asottile commentedon Jun 15, 2019
the hash cache only affects the shell and has nothing to do with pre-commit here, I'd suggest reading the manual for your particular shell (for instance bash or zsh) describing the shell builtins and how they function -- the shell is a program like any other and the hash cache is stored in that program's memory. pre-commit does not have access to that.
lokesh1729 commentedon Jun 15, 2019
@asottile I have gone through
hash. Agreed to your points... So, it is good to usehash -rcommand before invokingpylint?asottile commentedon Jun 15, 2019
there's no point to doing that in
pre-commit's configuration -- I was only suggesting you try that in your shell to establish an amount of sanity / correct expectations for what pre-commit is going to do (while eliminating the cache invalidation problem of your shell)miry commentedon Nov 21, 2019
Just in case:
I had next hook:
I use
poetry. And on each changes of tests, it used different python.Base on comment @asottile , I changed
language: pythontolanguage: systemand everything started works.scop commentedon Jan 13, 2020
Another tool that is affected by this problem is mypy, it really needs stuff from the project's venv to work properly unless one pretty much completely ignores errors resulting from imports.
I think the
systemworkaround isn't really applicable for cases where for example a git GUI tool (such as GitHub Desktop) is launched and stuff committed to a pre-commit enabled working dir; there's likely no opportunity to activate the project venv there.Maybe a
scriptapproach that would activate the venv would work, dunno. But a problem there is that the way people use venvs differ; some prefer plain python -m venvs, others pyenv virtualenvs, the dirs where the venvs are set up vary, so the script would not be able to activate the venv properly in all cases, and forcing people to use virtualenvs some specific way would be kind of meh.So it would be great if pre-commit would offer some more assistance in coping with these scenarios. Don't know what exactly would that be. Maybe e.g. some option that would "record" the currently active virtualenv settings at hook install time and use them later to construct the same environment when running the installed hook, dunno.