[HN Gopher] Show HN: A New Python Linter
       ___________________________________________________________________
        
       Show HN: A New Python Linter
        
       Author : latrova
       Score  : 62 points
       Date   : 2021-07-08 11:51 UTC (11 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | underyx wrote:
       | Shameless plug of Semgrep, a project I'm working on:
       | 
       | 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!
        
         | mdaniel wrote:
         | I would value knowing how to flag the _lack_ of usage of the
         | variable within the catch body; I had this problem while trying
         | to write a semgrep rule for Java, but if I were working with
         | python, it'd make me just as mad
         | 
         | 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 block
        
           | underyx wrote:
           | If I understand the comment right, the solution is to first
           | use pattern-inside to mark that you're interested in all try-
           | except blocks, and then pattern-not-inside to remove the ones
           | that correctly reraise the exception. Could you check this
           | out? https://semgrep.dev/s/underyx:always-reraise
           | 
           | If this helped, I recommend joining https://r2c.dev/slack --
           | we've got a #rules channel dedicated to solving problems like
           | this :)
        
             | stavros wrote:
             | As I understood it, the GP is looking to flag the non-usage
             | of the "ke" variable in the exception body.
        
               | whitten wrote:
               | Why did the approach mentioned not work? 1) recognize all
               | exception bodies. 2) recognize all exception bodies that
               | use the ke variable and mark them as okay.
               | 
               | Anything left should be an exception body that didn't use
               | the ke variable, right ?
        
               | stavros wrote:
               | It sounds correct (as long as it can distinguish between
               | use and shadowing), but the GP talked about reraising so
               | I wasn't sure if they meant the same thing.
        
         | stavros wrote:
         | Semgrep looks great, is it free/OSS? I don't know how powerful
         | it is from looking at the front page, but if it can replace
         | many of our tools, I'd love to use it as a pre-commit hook with
         | pre-commit.com.
        
           | underyx wrote:
           | Totally, it's LGPL licensed!
           | https://github.com/returntocorp/semgrep
           | 
           | Here's how to use it with pre-commit:
           | https://semgrep.dev/docs/extensions/#pre-commit
        
             | stavros wrote:
             | Well that's awesome, I'm very excited about this now! Thank
             | you for working on it!
        
               | underyx wrote:
               | Yay! I'm based out of Athens right now so (just a shot in
               | the dark but) if you happen to be here also, drop by The
               | Cube and I'll help you get set up with it :D
        
               | stavros wrote:
               | Ah, unfortunately I'm in Thessaloniki, but tell Stavros
               | he'll never have my Twitter handle!
               | 
               | And obviously if you're ever around these parts, I'd love
               | to have a drink!
        
               | underyx wrote:
               | Haha, I'll tell that to all the Stavroses I meet from now
               | on.
               | 
               | Cheers!
        
       | kseistrup wrote:
       | It's somewhat confusing that there is already a `triceratops`
       | python module: https://pypi.org/project/triceratops/
        
       | foxbee wrote:
       | Great timing. I was just looking into pylint
        
       | yewenjie wrote:
       | How does it compare with the existing linters?
        
         | latrova wrote:
         | AFAICT There's no linter that helps you with try/except blocks
         | leading people to be inconsistent across the project
        
           | 0x008 wrote:
           | Well most linters tell you not to use a bare except :)
        
       | stinos wrote:
       | The animation shows replacing 'raise ex' with 'raise' but just
       | leaves the now unused ex in 'catch Exception as ex', and in one
       | of the comments here there's a similar unused name ke in
       | 'Keyerror as ke'. That hurts my C++ eyes a bit, but not having
       | studied a lot of other Python code I wonder: is this a common
       | thing to do, or are these just 2 'bad' examples?
        
         | latrova wrote:
         | It's 2 bad examples, this linter is focused on try/except
         | blocks. For more general linting I would suggest flake8 :)
         | 
         | I'm planning on writing a plugin for it.
        
       | tomas789 wrote:
       | What is an advantage of this over just writing new rules to
       | something like Pylint?
        
       | red_hare wrote:
       | Very cool! I'd open a PR to implement it in our Python repo today
       | if it were a flake8 plugin.
       | 
       | 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.
        
         | Chiron1991 wrote:
         | I second this.
        
         | latrova wrote:
         | I'm so glad to hear that. It should be very simple to implement
         | a plugin. Once I'm able to, I'll create it and let you know! :)
        
       | bpicolo wrote:
       | I'd recommend a trigger comment word other than `notc`. It's hard
       | to remember - not obvious why it would be `notc` in the first
       | place.
       | 
       | But also it would be pronounced like "nazi"? Not exactly the
       | association you might be looking for.
        
         | _ZeD_ wrote:
         | I'm not a native english speaker... but how in the world "notc"
         | sounds like "nazi"?
         | 
         | I'll try to pronounce it like "no-ti-see", or "notick" or
         | something like that
        
           | jldugger wrote:
           | In my eyes, the options are "no tc" and "not c". "Not c" is
           | pronounced exactly the same as the german loanword.
        
           | johnisgood wrote:
           | If I say it as "not C", then it does not sound much like
           | "nazi", but if I say it quickly, then it could be mistaken
           | for "nazi". I am not a native English speaker either.
        
           | warent wrote:
           | I'm a native American English speaker and automatically read
           | notc as "not-see" which is the same pronunciation as nazi
        
         | latrova wrote:
         | Wow! Good catch! The idea was to be: "NO Try Catch" and
         | somewhat recall "NO T ry C eratops", but now that you pointed
         | out it doesn't make sense sense and it's not intuitive.
         | 
         | What do you think about "notry"?
        
           | themanmaran wrote:
           | notry is better. But why not be more verbose and just have
           | `no_try_catch`
        
             | eddyg wrote:
             | Why not the standard `noqa`?
        
       | jackhalford wrote:
       | Nobody seems to have touched on this so I'll go.
       | 
       | 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.
       | 
       | [1] https://pypi.org/project/result/
        
         | stavros wrote:
         | That's nice, thanks for reminding me of this. I will use it for
         | a project, as I, too, very much like the way Rust does error
         | handling.
        
       | rzarate wrote:
       | I'd love to see a violation category for error handling (ie.
       | making sure that all exceptions are handled appropriately).
       | 
       | 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).
        
       | murgindrag wrote:
       | I want a linter where I can exclude specific things in some kind
       | of configuration. Having linter hash tags in files is:
       | 
       | - 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.
        
         | zzzeek wrote:
         | flake8 is the most common linter front end and allows for
         | exclusions/inclusions in configuration files.
         | 
         | https://flake8.pycqa.org/en/latest/user/configuration.html
        
       | neolog wrote:
       | Why not just add a rule to pylint or flake8?
        
       | woodrowbarlow wrote:
       | it looks like this linter is _specifically_ for try/except
       | linting. do you anticipate users will use this alongside another
       | linter? or do you plan to grow into a full linter?
        
         | [deleted]
        
         | lorey wrote:
         | I would love to see this as a flake8 extension. An own tool
         | with own formatting, workflow, etc.? I'm not sure...
        
         | latrova wrote:
         | Today I use black (formatting), isort (import sort), and flake8
         | (general linting) for all my projects. I imagine people
         | combining tryceratops power with other linters as well since
         | the focus is very narrow.
         | 
         | I'll create a flake8 plugin soon since some people are
         | interested in it.
        
       ___________________________________________________________________
       (page generated 2021-07-08 23:03 UTC)