16 ms·
Why Pylint Is Both Useful and Unusable, and How You Can Actually Use It
- hadriendavid 10y agoI use pylama, it wraps pylint and other code checking tools. It also has a cool logo. https://github.com/klen/pylama https://github.com/klen/pylama
- pech0rin 10y agoUsing pylint in emacs with flycheck has saved me more hours that I can count debugging simple syntax and other python errors. As the author mentions, whitelisting is the only way to go. http://www.flycheck.org/en/latest/ http://www.flycheck.org/en/latest/
- blacksmythe 10y ago>> configure it with a whitelist of lint checks I have found Pylint to be very helpful, and always use it on Python projects past the size of a short script. If you start using Pylint at the beginning of a project, you can blacklist warnings that you want to ignore fairly easily.
- guitarbill 10y agoAgreed. I mean you don't need to follow it religiously, but it's pretty workable with a small blacklist (docstrings, locally disabled, and a few more I don't instantly recall). When the hacks get bad, maybe the odd `#pylint: disable=<blah>` but at least it shows you've though about the pylint warning. >> If the errors are utter garbage, delete those checks from the configuration. I've never had "utter garbage" errors. I've had errors I didn't agree with/made the conscious choice to ignore. And I've had errors I thought were garbage, but really I just didn't fully understand them. (Although I've experienced how applying it to an existing, unlinted project is horrible.)
- _Wintermute 10y ago>> I've never had "utter garbage" errors. I've had errors I didn't agree with/made the conscious choice to ignore. And I've had errors I thought were garbage, but really I just didn't fully understand them. I've had lots of spurious errors with numpy code, where pylint seems to think everything is a list.
- guitarbill 10y agoI've experienced something similar with SQLAlchemy, because of the amount of auto-magical stuff it does. I solved it with `[TYPECHECK] generated-members=query`, although this can be slow so blacklisting SQLAlchemy classes is still an option. NumPy's `__init__.py` code is a bit messy and manipulates `__all__` a lot [0], so I can understand why pylint (and other static code analysis tools) struggle with it. Yeah, it's frustrating hitting errors in third party modules, and it's tedious for one person initially to set up pylint correctly, but so worth it for a team. [0] https://github.com/numpy/numpy/blob/master/numpy/__init__.py#L183 https://github.com/numpy/numpy/blob/master/numpy/__init__.py...
- denfromufa 10y agoPylint is usable in PTVS projects: https://github.com/Microsoft/PTVS/wiki/Pylint https://github.com/Microsoft/PTVS/wiki/Pylint Also Sublime Text has a nice plugin called Anaconda, where PyLint can be enabled: http://damnwidget.github.io/anaconda/IDE/ http://damnwidget.github.io/anaconda/IDE/
- syllogism 10y agoI find frosted (a reimplementation of PyFlakes) very useful.
- mhb 10y agoHow does Pylint compare with PyCharm's inspections? Is there a reason to use both?
- brianwawok 10y agoI think PyCharm is closer to flake8. It's good but more surface level.
- masklinn 10y ago> I think PyCharm is closer to flake8. PyCharm does type inference and cross-file name/object checking, these are not things pyflake can do.
- brianwawok 10y agoAh that is true, it does a little bit of MyPy stuff too.
- tclancy 10y agoPretty sure PyCharm uses pep8 under the hood. It's more permissive than pylint.
- xapata 10y agoWhy weren't the functionality of those for-loops tested? It seems like a strange thing to burden PyLint with catching.
- brazzledazzle 10y agoWell the blog is called Code Without Rules so...
- itamarst 10y agoThe thought was that in the end you need principles, not rules. "Always write unit tests" is a good rule, and this code actually had unit tests, but the truth is some code doesn't need unit tests. That being said, since you seem like a suitably snarky person, you may enjoy the email newsletter I write which is dedicated to the ways I've screwed up: https://softwareclown.com/ https://softwareclown.com/
- brazzledazzle 10y agoI try to keep the snark at bay but it probably still wins more than it should. I signed up. Better to learn from your mistakes than my own. The testimonial was awesome by the way.
- itamarst 10y agoThey were tested, but it didn't catch the bug unfortunately. I got bit by the fact that I was testing with synchronous callbacks, but real world use was with asynchronous callbacks.
- OskarS 10y agoIt's an extremely common bug in basically all languages that allow closures to capture "live" variables. Almost every developer that uses any kind of async programming gets bit by it eventually. For-loops and closures do not mix well.
- ibizaman 10y agoI have pylint integrated with vim through syntastic. The amount of output is thus limited to one module at a time and not intrusive nor disruptive. Also, like suggested, I have blacklisted some errors I don't care about.
- cheiVia0 10y agoSome more Python checkers: https://anonscm.debian.org/cgit/collab-maint/check-all-the-things.git/tree/data/python https://anonscm.debian.org/cgit/collab-maint/check-all-the-t...
- asah 10y agobig +1 for pylint - saved my butt more times than I can count. Subtly, pylint makes it easy to migrate teams to new APIs, e.g. by disallow rules. -1 to whitelisting - I just devote ~1 day to going through the 1000s of warnings, not a big deal.
- hueving 10y agoWhat would be awesome is an interactive pylint onboarding tool. Start with no checks, then add them iteratively in order of severity showing the new errors at each step asking if the rule should be adopted or not.
- raymondh 10y agoFWIW, I like pyflakes because of its design philosophy: """ Pyflakes makes a simple promise: it will never complain about style, and it will try very, very hard to never emit false positives. Pyflakes is also faster than Pylint or Pychecker. This is largely because Pyflakes only examines the syntax tree of each file individually. As a consequence, Pyflakes is more limited in the types of things it can check. """
- mplewis 10y agoI love Pyflakes. And when I do care about style, I like Flake8 – it has sane defaults and lets me override them on a flexible basis.
- gbog 10y agoI have pyflakes as a vim plugin but it is not enough on big projects, as it will not catch import errors (which are the most common code-breaking errors)
- masklinn 10y agoYep pyflakes is fast, but the tradeoff is that it does relatively little and can't do cross-file inspections or anything somewhat complex. It's also even less well-documented, I have no idea if you can even create a custom flake. pylint's got the advantage that as badly documented as it is creating custom lints is supported and normal.
- shoo 10y agoagreed. i've found pyflakes easier to work with than pylint in terms of setting a minimum standard to enforce (in terms of running it with CI and using it to mark builds as unstable if there are any warnings/errors). pylint warns about various things that arguably aren't actual problems but might be good to clean up in some cases. yes, most of these can be disabled by configuration. on the other hand if you see pyflakes output it tends to indicate something is unambigiously wrong.
- sametmax 10y agoAlso, now you can use mypy for additional bug catching. Personally I have flake8 + mypy + py.test + coverall + mccabe, running on all my new projects.
- w_t_payne 10y agoI've really got to start using mypy. Looks awesome.
- sametmax 10y agoThe concept is awesome, the implementation is less so. There is still a lot of ironing to do. But it does catch many bugs, even when you don't annotate anything. Anyway, it's good tu use it early to help guido fix the last warts.
- aaronbasssett 10y agoYour unittests should have caught both of those errors. Also Django does use linting: https://github.com/django/django/blob/master/tox.ini#L36-L41 https://github.com/django/django/blob/master/tox.ini#L36-L41
- itamarst 10y agoI did have unit tests, they didn't catch the bug though because the test used a synchronous callback (much easier to test) and real world code ran with async callback.
- itamarst 10y agoRe django using linting, it uses flake8, which is a nice tool... but misses lots of stuff pylint catches.
- thedonkeycometh 10y agoShould have unit tested that title
- tome 10y agoOh Pylint, used by my previous employer as though it were a type checker. Now I just use a language with a type checker.
- m_mueller 10y agoIMO the first example is a good use of a linter, the second should be caught by coding style: I keep the following rule with closures: 1.) Never go more than two layers deep, so you have one "def" inside another "def" and that's it. 2.) Always keep the closures at the top of the containing function - no further nesting. Kids today coming from Javascript seem to be way too much in love with their closures. Example: def my_routine(flag, foo): def my_little_helper(thingy) if flag: return thingy return thingy + foo return my_little_helper(42)
- chrisseaton 10y agoWhat do you think is the problem with deeper nester closures? Do you really think that a little extra lambda or something inside your helper function would suddenly make it unreadable?
- m_mueller 10y agoI just think multiple levels of capturing scope into closures is a bad idea - if something with that state goes wrong it will be hard to debug. I also hardly see any benefit in doing it.
- nilliams 10y agoNo need to pick on 'kids' from JS thanks, and actually in my interviewing experience a lot of JS devs don't actually know what a closure is. Worrying, but true.
- m_mueller 10y agoYeah, sorry for the sarcastic tone. It's just that JS/Ajax comes to mind when I think about deeply nested closure pasta. Here's hoping that ES201x will teach async/await to a wide audience soon.
- itamarst 10y agoThat's a good idea, but you shouldn't assume too much about why people are making mistakes, I've been working with and on Twisted since 2001, so not a JS kiddy :) I made this mistake fair and square even though I knew better. Which is why I like having a tool that'll catch it for me. My typical approach is `lambda x, y=y: f(x, y)`. Inline functions are much more readable, is the thing. But I can see the argument for this coding style, yes.
- michaelmcmillan 10y agoThe fact that your unit tests did not catch that bug tells me you should spend less time linting and more time covering.
- crdoconnor 10y agoLinting is cheaper than unit testing.
- michaelmcmillan 10y agoThey serve two completely different purposes. Testing ensures the logic is correct, linting is checking the code for syntax errors and bad practice.
- crdoconnor 10y agoThey don't actually serve completely different purposes. Linting is supposed to catch bugs and it does, very cheaply. It just doesn't catch very many. In terms of coding style I've found it fairly ineffective at picking up on most bad practices.
- michaelmcmillan 10y agoSure, "bug" as a term is as elastic as you want it to be. I was pointing out the false equivalence you implied by writing "cheaper". Testing and linting does not provide the same value, so calling it cheaper is a kind of fallacy.
- crdoconnor 10y agoThat's like saying that a sandwich and a condo do not provide the same value so calling a sandwich cheaper is a kind of fallacy. There's no elasticity over what the OP reported. That was a bug. Clear as they come. He may have unit tests but they didn't catch it and since you can't have unit tests for everything, why not spend 500ms running a linter?
- crdoconnor 10y agoI actually think that unused initialized variables might be the only pylint rule which has found genuine production bugs for me.
- Xophmeister 10y agoVim + Syntastic + pylint + mypy works pretty well for me
- frobozz 10y ago> The real problem is the amount of output. That's a nonsense argument. The output is proportional to the number of violations. If you keep your project clean, then you'll have about a report of about 100 lines showing the statistics, with a one-line message: "your code has been rated at 10/10" at the end. Of course, if you run it against a project that is full of violations, you'll get lots of violation messages. If you use a linter or style checker or whatever on a project that is full of violations, and it doesn't produce lots of output, then the tool is rubbish. The right thing to do is to use it from the start and either fix or explicitly ignore any violations as they come up. The approach described in this blog post is good for existing projects. However, the linked examples disable all warnings and explicitly enable those that the author has deemed important. It would be better to explicitly disable unimportant ones.
- raverbashing 10y agoBut having classes without an __init__ method is not a violation. (Amongst several other warnings, like number of methods, size of methods, etc) Don't get me wrong, pylint is great, but at some point it seems those tools begin to get opinionated instead of helping
- frobozz 10y agoIt's a violation of someone's coding standard, if it's not a violation of yours, then add it to your exclusion list. The point of the __init__ warning is that a class without a constructor might not really be a class at all. Number and size of methods are both code smells that indicate that you might be creating a God Objects or God functions. If there's a good reason for an enormous function, then violations can be ignored on a case, project or user basis. I'd prefer a tool that can warn about that sort of thing, to one that can't. Most of the limits are configurable, and set to reasonably generous defaults. Methods per class is 20 and statements per method is 50.
- raverbashing 10y ago> is that a class without a constructor might not really be a class at all. Yes, but there are several cases where this doesn't matter. Exceptions for example. > Number and size of methods are both code smells Yes, but Pylint's default values comes from where? It's pretty easy to trigger them without being a God object 20 methods is fine, 50 lines is what fits in my terminal, and a lot of functions don't fit that (especially with blank lines, splitting lines bigger than 80 chars, etc
- wink 10y agoI'm also not a fan of pylint - and I just ran it against a small project, 812 LOC of Python. The code was developed with help of the 'pep8' tool which only finds 12 offending lines we declared ok (all too long, but it's SQL where formatting for readability is more important than 80 chars..) and wlthough pylint gives it a 7.26/10 there's a TON of errors. So yes, I could now spend a while blacklisting some rules, fixing some stuff, but the defaults are just so bad I won't bother, as usual. Because I think the shape of this code is pretty good.
- itamarst 10y agoThis is why I suggested whitelisting, because it's much faster. So you get some checks going immediately.
- falcolas 10y agoThe problem with whitelisting is that you never un-whitelist some things, depriving yourself of potentially useful feedback. Better to run with -E first, address those, then add in warnings, and blacklist the points which are actually not useful.
- gbog 10y agoI beg to differ, to me python (and js) are tolerable languages only when they are controlled with linters and automated tests. And pylint is still the best linter for big python projects. It is fully configurable and each and every default rule is interesting per se. For example, a class with no `__init__`, no or too many attributes or methods, is a potential code smell and candidate for refactoring.
- tbronchain 10y agoI'm using pylint daily and usually running it doing "pylint -E" to discard warnings and display errors only. I found it very useful, and never seen a case of an error that shouldn't be fixes. I'm probably missing some useful warnings, but it's always better than not using it at all, or getting lost with thousands of them!
- itamarst 10y agoThe problem is the bug that pylint caught that would've gone to production was caught by a warning, not an error. So we could've just used -E but then we'd miss out on some very useful stuff.
- deleted 10y ago[deleted]
- tbronchain 10y agoThat's a fair point I agree. The pylint config files in the article are great, and I will definitely look into it. Pylint -E is more like a quick lazy option to make 80% of the work :)
- w_t_payne 10y agoI've got my pylint set up to work right from the very beginning of the project, with most rules enabled, so I can keep a fairly clean linted state and never have to play "catch up" to any great extent. (I also use pep8; pep256 and radon to control formatting; documentation and complexity). I wrote a wrapper around all of these linting tools so I can call them programmatically from the same Python process, and each time a file changes I run my linting tools on that file. (Not the whole codebase). I also intercept the output and re-format it to give a (brief) report in a common format. I can also control thestrictness of linting (i.e. which config file to use) with a YAML-format comment in the file header, so we can write quick prototypes without worrying too much about style then bring them into the quality system on a file-by-file basis. In this way, linting runs fast enough to be useful in the edit-lint/test loop, and the error messages are both telegraphic and immediately actionable.
- kshitij10496 10y agoThis sounds really cool. In my open source projects, like you, even I run pylint on each module instead of the entire codebase. However, often I run into errors with regards to handling outputs. Can you share your config (as a GitHub gist) so that I can learn more about your approach ?
- Mithaldu 10y agoDoes python not have anything like `use strict;`? Edit: What the heck, this is an honest question. :(
- falcolas 10y agoNo, but even a use strict type clause wouldn't have helped in this case, since the entire syntax was valid (volme was being properly initialized as part of the loop definition). The error occurred because 'volume' was considered to be part of the function scope, despite being defined as part of the for loop scope. To me, it's a good reason to use different loop variable names for successive loops. A good practice to use in any language, since it helps keep loops distinct to your mind as well. for volume in get_volumes(): print volume for other_volme in get_other_volumes(): print other_volume The second loop throws a runtime error and an error level linting alert.
- Mithaldu 10y agoIt would help if it restricted variables auto-vivified in a loop declaration to the body of the loop itself, instead of making it available for the entire parent scope. I'm used to working with languages that behave like that. I'm actually a little surprised something like that hasn't been attempted yet for Python. Thanks for the answer!
- riquito 10y agoI've been quite happy with pycodestyle (formerly pep8), using autopep8 to automatically apply fixes when deemed safe.
- narendrapateljc 10y agoHave pylint integrated with sublime and configured it to run checks on every save. Its really a life saver! Plus if you want to disable some features you can even do it at the local level rather then having a single central rule. eg:- using #pylint: disable=I0011,E0401 in your .py files.
- narendrapateljc 10y agoHave pylint integrated with sublime and configured it to run checks on every save. Its really a life saver! Plus if you want to disable some features you can even do it at the local level rather then having a single central rule. eg:- using #pylint: disable=I0011,E0401 in your .py files.
- tclancy 10y agoLike an article that is both for and against it's subject, I agree and disagree. Pylint is painful for the author this time but it should get easier and easier with each project. If you use pylint from the start you won't get bitten by that "mountain of errors" problem and you develop a default .pylintrc file that you can bring from project to project, tweaking as needed. A (hopefully) short story: on my last contact job I was brought in as a senior developer for a growing Django project that had 0 coding standards. I imposed a small set of coding rules (what I actually did was say we would follow the Django coding style guide, as I've learned over time pointing at an existing, 3rd-party guide means when people get butthurt about having to change a habit they get mad at the guide and not at a coworker) and then added pep8 and pylint to the git commit hook. That lasted for about a day because of the massive amount of errors from pylint (the situation the author describes), so I modified the hook to block on pep8 violations and just give a guilt-trip in pylint errors for the time being. Then myself and a couple of team members who drew short straws sucked it up and actually fixed pylint errors as we went until we could turn pylint back on. We fixed a number of bugs in that process and then going forward pylint started to show it's strength by catching the sorts of bugs the article mentions (and still enforcing a standard coding style so you could no longer look at a file and say "That's tclancy's code" but rather saw "That's our-project's code". Having pylint in the hook process is better than running it as part of CI or ad hoc because it's run more often, meaning you get hit with a smaller number of errors at once. Having pylint or pep8 as part of your IDE is even better because you fix problems as you go. One of my current side projects is a Django project written by a Rails dev (no idea why) that I do maintenance on. The first thing I do when I open a file I've never seen is get rid of all the red and green squigly lines (or at least look at why they are there). Most of the time it's just a formatting issue but there are plenty of actual bugs it turns up. Just by opening the files to make an update or fix a bug I often wind up improving things people didn't even know were broken as a side effect. Linters are a good thing. They can always be better and they will always annoy for some idiosyncrasy they have (or you do), but it beats ignorance.
- draw_down 10y agoI think a formatter is really best. Linters know what's wrong, the obvious next step is to just fix it. I'm not interested in thinking about this stuff anymore, just fix it so I can move on.