Show HN: A New Python Linter
github.com
github.com
But also it would be pronounced like "nazi"? Not exactly the association you might be looking for.
What do you think about "notry"?
I'll try to pronounce it like "no-ti-see", or "notick" or something like that
Since trying rust's ans zig's error system I have avoided using python exception and replaced them with [1]. Basically functions return Ok(value) or Err(msg). callers can then get the result with `a = fn(a,b).unroll()`. For Ok it will unwrap and for Err it will return early (with a decorator to catch the virtual exception). The only overhead is the decorator, apart from this the ergonomics are the same as rust.
It's so nice I hope it becomes a part of the language some day.
Where exceptions are problematic for my code base is when unwinding the stack is not the best behavior. Sometimes, I want the following lines to execute and sometimes I want to retry. Exceptions make a default execution flow very easy, but the default is sometimes wrong, and the correct flow is not considered until undesirable behavior appears at runtime. An (ok, result) system seems like it might force one to think more carefully about the proper flow. But the price is always having to think about it, when stack unwinding is usually the right flow. Or one might be tempted to create a Haskell like monad that automatically skips execution of following expressions, leading to the same bugs as an exception system. Correct code is possible with either system. I’m undecided if one system is more likely to lead to correct code.
For example, `for` loops are implemented by throwing a `StopIteration` exception when they're finished.
I like explicit code.
I just wrote this Semgrep rule in 45 seconds which replicates the TC201 rule from tryceratops: https://semgrep.dev/s/underyx:tc201
Writing this rule was definitely easier than writing a new linter package, but also easier than writing a custom pylint or flake8 plugin.
Message to OP: we are working on making Semgrep's rule registry (https://semgrep.dev/explore) a much more community-centric place. Our vision is that just like you could publish the GitHub repo of guilatrova/tryceratops, you could publish a set of Semgrep rules under your name and brand. Semgrep users could then find and use your lightweight "linter" via the registry. If you're interested in being one of the first authors to publish on the Semgrep Registry, please reach out at bence@returntocorp.com!
I'll show the Java version first, because it's more obvious, then the equivalent in Python to stay moderately on-topic
try {
((String)null).length();
} catch (Exception e) {
System.err.println("I swallow errors for fun!");
}
try:
dict()["kaboom"]
except KeyError as ke:
logger.error("it did not work")
In the python version, I would accept the presence of `logger.exception` but my question in both code snippets is how to detect that nothing touches the exception variable within the catch blockIf this helped, I recommend joining https://r2c.dev/slack — we've got a #rules channel dedicated to solving problems like this :)
Anything left should be an exception body that didn't use the ke variable, right ?
Here's how to use it with pre-commit: https://semgrep.dev/docs/extensions/#pre-commit
And obviously if you're ever around these parts, I'd love to have a drink!
Cheers!
As a separate linter, I have to update the half-a-dozen places my current linters (flake8 and mypy) get applied (Makefile, .vscode/settings.json, shared vimrc, cloudbuild.yaml, etc.)
If it were a flake8 plugin, I can just pip install and select the TC codes in my .flake8 configuration.
I'll create a flake8 plugin soon since some people are interested in it.
- Ugly and makes for less readable code.
- Doesn't apply the same exception in similar situations. If I made an exception to a rule in one place, I'd like it to apply everywhere I have similar contexts.
- Gives a binary good / bad. A linter ought to flag questionable choices to look at -- not just bad choices. At that point, I should be able to say "oops, let me fix that," or "I meant to do that" without leaving garbage around.
I'd like this stuff in a linter config/data file, ideally, as semantically as possible. If I tell a spell checker that yes, in fact, my company name is a misspelling of a common word, I don't need to tell it twice. This is not true for linters.
I'd also like all the linty stuff configured in one place, with one syntax. Not pylint + pycodestyle.
Examples of use-cases:
- A lot of the times when I'm building an API, I have unused arguments in interim versions of the code. That's okay.
- I occasionally break Python naming conventions, especially when either planning a code refactor, or when trying to comply with non-Python naming conventions
- I generally dislike lines > 80 characters, but often, that's the right way to do things, especially if the alternative is breaking up a long token like a URL.
And so on. I'd like all of these caught, and then I'd like to give the linter a pat on the back, say "Thank you, but here it's okay," and move on with my life.
There's actually a lot more stuff which could be put into a linter once you assume that being wrong even 80% of the time still makes for a helpful tool. If a linter flags give things and I change one as a result, that's well worth the little bit of hassle. If a linter keeps flagging those things forever, it's no longer worth the hassle.
I'm planning on writing a plugin for it.
After using Golang for an extended period of time, coming back to Python has been frustrating at times because it is not as straight forward to handle errors/exceptions (especially when using external libraries that aren't well documented).