Summary
When you are writing code it is all to easy to introduce subtle bugs or leave behind unused code. Unused variables, unused imports, overly complex logic, etc. If you are careful and diligent you can find these problems yourself, but isn’t that what computers are supposed to help you with? Thankfully Python has a wealth of tools that will work with you to keep your code clean and maintainable. In this episode Anthony Sottile explores Flake8, one of the most popular options for identifying those problematic lines of code. He shares how he became involved in the project and took over as maintainer and explains the different categories of code quality tooling and how Flake8 compares to other static analyzers. He also discusses the ecosystem of plugins that have grown up around it, including some detailed examples of how you can write your own (and why you might want to).
Announcements
- Hello and welcome to Podcast.__init__, the podcast about Python and the people who make it great.
- When you’re ready to launch your next app or want to try a project you hear about on the show, you’ll need somewhere to deploy it, so take a look at our friends over at Linode. With the launch of their managed Kubernetes platform it’s easy to get started with the next generation of deployment and scaling, powered by the battle tested Linode platform, including simple pricing, node balancers, 40Gbit networking, dedicated CPU and GPU instances, and worldwide data centers. Go to pythonpodcast.com/linode and get a $100 credit to try out a Kubernetes cluster of your own. And don’t forget to thank them for their continued support of this show!
- We’ve all been asked to help with an ad-hoc request for data by the sales and marketing team. Then it becomes a critical report that they need updated every week or every day. Then what do you do? Send a CSV via email? Write some Python scripts to automate it? But what about incremental sync, API quotas, error handling, and all of the other details that eat up your time? Today, there is a better way. With Census, just write SQL or plug in your dbt models and start syncing your cloud warehouse to SaaS applications like Salesforce, Marketo, Hubspot, and many more. Go to pythonpodcast.com/census today to get a free 14-day trial.
- Your host as usual is Tobias Macey and today I’m interviewing Anthony Sottile about Flake8
Interview
- Introductions
- How did you get introduced to Python?
- Can you start by giving an overview of what Flake8 is and how you got involved with the project?
- There are a variety of tools available for checking or enforcing code quality. How would you characterize Flake8 in comparison to the other options?
- What do you see as the motivating factors for individuals or teams to integrate static analysis/linting in their toolchain and workflow?
- What are some of the challenges that might prevent someone from adopting something like Flake8?
- How can developers add Flake8 to an existing project without spending hours or days fixing all of the violations?
- Can you describe the overall design and implementation of Flake8?
- How has the design and goals of the project changed or evolved?
- There are a wide array of plugins for Flake8. What is involved in adding new functionality or linting rules?
- What capabilities does Flake8 provide that make it a viable platform for building plugins?
- What are some of the limitations of Flake8 as a platform?
- What do you see as the factors that have contributed to the widespread usage of Flake8 and the large number of available plugins?
- What challenges does that pose as a maintainer of Flake8?
- What are some of the other tools that you see developers use alongside Flake8 to help manage code quality and style enforcement?
- What are some of the most interesting, innovative, or unexpected ways that you have seen Flake8 and its plugin ecosystem used?
- What are the most interesting, unexpected, or challenging lessons that you have learned while working on Flake8?
- When is Flake8 the wrong choice?
- What do you have planned for the future of Flake8?
Keep In Touch
- @codewithanthony on Twitter
- asottile on GitHub
Picks
- Tobias
- SEVENEVES by Neal Stephenson
- Anthony
Closing Announcements
- Thank you for listening! Don’t forget to check out our other show, the Data Engineering Podcast for the latest on modern data management.
- Visit the site to subscribe to the show, sign up for the mailing list, and read the show notes.
- If you’ve learned something or tried out a project from the show then tell us about it! Email hosts@podcastinit.com) with your story.
- To help other people find the show please leave a review on iTunes and tell your friends and co-workers
- Join the community in the new Zulip chat workspace at pythonpodcast.com/chat
Links
- Flake8
- PyFlakes
- PyCodestyle
- McCabe
- pre-commit
- PEP 484
- MyPy
- Pylance
- Pyright
- Pylint
- Black
- yapf
- autopep8
- pyupgrade
- isort
- reorder-python-imports
- Static Analysis
- pydocstyle
- autoflake
- pyproject.toml
- Abstract Syntax Tree
- Concrete Syntax Tree
- Dagster
The intro and outro music is from Requiem for a Fish The Freak Fandango Orchestra / CC BY-SA
Hello, and welcome to podcast dot in it, the podcast about Python and the people who make it great. When you're ready to launch your next app or want to try a project you hear about on the show, you'll need somewhere to deploy it. So take a look at our friends over at Linode. With the launch of their managed Kubernetes platform, it's easy to get started with the next generation of deployment and scaling powered by the battle tested Linode platform, including simple pricing, node balancers, 40 gigabit networking, dedicated CPU and GPU instances, and worldwide data centers.
Go to python podcast.com/linode, that's l I n o d e, today and get a $100 credit to try out a Kubernetes cluster of your own. And don't forget to thank them for their continued support of this show. Your host as usual is Tobias Macy. And today, I'm interviewing Anthony Sottile about Flake8 and how you can use it for being able to enforce style guides for your Python code. So, Anthony, can you start by introducing yourself? My name is Anthony. I have been working with Python for,
[00:01:11] Unknown:
oh, about 9 years now. I've been programming for almost 20, and, yeah, I'm the current core maintainer of Flak8. And a little bit about myself, I really like code quality and maintenance and auto formatting and all those things that come along with it. I'm really interested test frameworks, automation, and that sort of stuff, and so Flaky fits pretty naturally into all those goals that I really enjoy. And do you remember how you first got introduced to Python? Yeah. So the first project that I worked on Python was at university. I was 1 of the instructors for an intro to programming course in c plus plus and the auto grader at the time was written in Python.
And the original author of the auto grader graduated, and we needed some much needed improvements to how it functioned and worked and, you know, validated code and did that safely. And, rewrote a lot of the back end and improved some of the front end. That was actually my first touch at Django as well, In Python 2.4, I think it was. It was a little bit dated because of Red Hat, but,
[00:02:16] Unknown:
it was my first touch at Python. Yeah. I think I started Python around that same same time frame at 2.4, 2.5 era. And as somebody who works at a university, auto graders are still a thing, and they are still probably just as bad as you remember.
[00:02:30] Unknown:
Yep. Yep. Yep. I tried to make it more pluggable and easy to use, but who knows? Although, I've heard someone ping me recently and was like, oh, yeah. We're still using your auto grader. It still works great.
[00:02:41] Unknown:
And so you mentioned that you're the current core maintainer of Flake 8. I know that it has a relatively long history, not as long as some of the other linting tools in the Python ecosystem and, of course, linting tools more broadly. But I'm wondering if you can just talk a bit about how you got involved with the project
[00:02:57] Unknown:
and sort of the transition period of becoming the current maintainer of it. So from what I understand, Flagan has had quite a storied history. It originally, as far as I understand, arose because Pie Flakes and Pycode style both solved a very similar but different set of linting goals, and there wasn't a great way to kind of centralize all of those linting rules into a single place and flake it kind of arose as the place to organize all those, along with McCabe, which is a complex state checker, and that was many years ago. I think I started maintaining it was 2017 or 2018, somewhere around there.
At the time, in a way, all 3 of the projects had kind of lagged in or lapsed in maintainership. You know, new Python versions had come out, and in a lot of ways, the tools had not quite caught up to all of that. And I was, you know, personally invested in this because I use Python on every you know, on a bunch of projects and I'm interested in linting. And I had a lot of experience working with linting tools and other related things because of pre commit and, you know, offered to chip it on some maintainership. Coincidentally, at the time, the core maintainer of, like, who was the only person working on it at that point, Ian, was going through a rough patch in open source and decided they wanted to step back from open source and assign out projects to people who are willing to maintain them. And so I stepped up for Flak8 and took over maintainership there, got a release out that hadn't been released, and I think it was a year and a half at that point and greatly needed a release.
The first release, you know, anytime you release software after it doesn't release for a while, it's a rough release. So I went through, I think, almost 8 or 9 bug fix follow ups to that release, kind of got my feet wet with all of the crazy amount of people that are using the project, but also the amount of complexity and the things necessary to make that happen. But, yeah, I've been maintaining ever since Ian stops in from time to time to also help out. Yeah. It's the tragedies of open source and success story in the same breath. Yeah. I definitely feel the pain that Ian had on the project. Being the only maintainer, you tend to take a lot of blunt trauma of everyone being unhappy with the status quo and, like, you know, also being unhappy when things change and the evolution of a very important Python project is
[00:05:27] Unknown:
tricky. In terms of the overall ecosystem, there are definitely a number of different tools available for being able to do things like linting and style enforcement and quality checks. And I'm wondering if you can just give a bit of your perspective of the landscape as a user of Flake8 and as a maintainer of Flake8 and somebody who's been using Python for a while and just some of the notable aspects of Flake8 that might lead somebody to use that either in addition to or instead of some of the other options?
[00:05:56] Unknown:
Sure. I usually try and classify the code quality tools in Python into about 4 categories. The first and probably the easiest to talk about is type checkers, those which you know, take your source, take annotated PEP 44 typings, and make some sort of assertions about the types of your code statically. So tools like MyPy or Pylance or IntelliJ in IDEs or Pyrite or, I don't know, 4 or 5 other type checkers. So we'll leave those in their own bucket. They necessarily need to have full access to all of your source code and all of your dependencies to have a holistic view of the types in play and to validate your code, and they validate a certain type of correctness, usually around, you know, am I dealing with the correct types? Am I making code that interfaces properly with other code? They're mostly concerned with the typing aspect of things.
There's also another set of linters and code frameworks which kind of bleed into that space, which is what I refer to as the dynamic analysis tools. And for those, I mostly consider tools like Pylint in this space where they also need access to your dependency because they also do some amount of type analysis. You know, Pylance is concerned somewhat with the typing stuff but also with, you know, your white space, your naming, your everything else about linting. Pylint also has some specialized tools for, like, Python 2 to Python 3 migration and some other stuff there as well.
That's kind of the dynamic bucket. Thirdly, we'll get to the flake bucket last because I feel like that makes the most sense. Thirdly, I like to talk about code formatters, and these can either be formatters that, you know, just manage white space, things like black or autopepay or yap I actually don't know how they pronounce it, y a p f, the Google project. These usually take code and, you know, have some sort of style guide and try and morph that code to fit that style guide. You know, in Black's case, it is a very rigid code for a matter. It has a very specific set of formatting rules and not really much configuration around how to set those. Whereas, on the other hand, you have a tool like Yap, which has something like 200 settings in order to create your own custom auto format or style guide. And then you have autopep8, which is a tool based around Pycode style, which used to be called PEP8, and that's why it's called autopep8. And it's it's a little confusing, but, yeah, so that's the code formatter bucket. There's also other code formatter, things like pyupgrade, which I wrote, or import sorters such as, like, isort or reorderpipine imports. I mean, they're mostly concerned with a very specific type of auto formatting.
You know, in the case of pyupgrade, it's syntax improvements. In the case of isort, it's import reordering. And then we get to the last bucket, which is your static analysis tools, like, purely static analysis tools. These are the ones that can run isolated from other things. They're basically just looking at a single source file at a time. They're picking out either programming mistakes like PyFlicks does or white space and code style things like PyCodeStyle does or PyDocstyle or other related tools there. It's like it kind of fits into well, if I think of it fitting most into the last space, technically, it can run tools from pretty much all the other spaces, less so the formatter space, but it can run tools in, you know, static analysis, dynamic analysis, or in the type checking phase.
I don't really think it fits well into that. I like to think of it as a static analysis tool. The other thing about static analysis tools is you can kind of run them in isolation from your code. They tend to be much faster than the other variants, and, you know, they're there to get you a quick check of some sanity and correctness on your code base. Does that answer the question?
[00:09:39] Unknown:
Yeah. No. It definitely does. Yeah. It definitely helps to sort of get a framing of I think those categorizations are very useful, particularly as people are starting think about, okay, what are the tools that I wanna bring into my development workflow? Or, you know, maybe I wanna pull in 1 from each bucket or maybe only need 1 from a couple of different buckets or sort of understanding, like, what are the dividing lines between these tools? Because as a newcomer, it can be difficult to understand, like, okay. This tool says it does these things, but what doesn't it do? So that's definitely helpful framing. Yeah. And I think a lot of the tools, even in the flake, 8 space, like, are very careful about setting their boundaries about what they do and do not check. Like PyFlakes, for instance, it can't really solve those typing situations. Like, it doesn't know the halting problem. It doesn't know the structure of your code. It can't really tell you whether something is gonna return none or not, and so it explicitly doesn't try and solve any of those cases. It's also briefly worth calling out. You mentioned your work on pre commit that you have been on the show previously back in 2018 to talk about that project. So we'll add a link at the show notes for anybody who wants to dig deeper on there. Maybe we'll have another follow-up episode sometime where we can talk about what's changed or what hasn't changed in that project. Yeah. Sounds great. I appreciate the work you've done there as well. And so in terms of Flake8, what do you see as being some of the major motivating factors for people deciding to use flake 8 in either addition to or instead of some of these other tools? And for people who don't yet have any sort of kind of quality enforcement, what are the sort of motivating factors that lead them down path of considering using something like flake8 or MyPy or what have you to start adding those types of enforcement rules?
[00:11:16] Unknown:
So for me, flaking it out of the box so we'll talk about flaking out of the box. I don't think that's, like, the huge selling point of flaking. That said, it is very good out of the box. Flake 8 out of the box combines a few common tools that are going to give you a really great baseline for validating that your code has some amount of correctness before you run it. It combines together PyFlakes, which is, you know, basic error checking, you know, undefined names, missing or extra imports, you know, common programming mistakes, and it also takes Pycode style, which is going to be your kind of white space checker. Like, is this indented properly, does this line look good to a human for a particular style choice. It takes those 2 tools, like, it actually doesn't implement any linting itself. It defers all of the linting to other tools, but it takes those 2 tools and wraps it into a framework that provides the necessary things that you would need on top of that. So things like necessary things that you would need on top of that, so things like inclusion and exclusion based on files, recursing through directories, a no QA comment so you can disable particular inline lens, per file ignores so that you can say, oh, yeah, well, this chunk of code is okay to violate this particular thing. Enabling and disabling various flags, you can say, like, oh, I don't care about missing or extra imports in these particular sets of files or, like, I I don't have this particular white space rule in this code base. Then it allows you to kind of configure that and and set that up and have a consistent linting experience in your repository.
I think the big selling point about flake 8 is that it's a framework that you can plug in a bunch of other linting tools into, and you can write your own linting tools that if you have very specific requirements I know at Lyft, we had a very domain specific requirement around our localization framework, and so we wrote a plug in which knows about the particular call structures of the, you know, the localization framework and made sure to enforce particular things there. I mean, also, you can take a lot of the other open source linting tools in Python and a lot of them already provide flake 8 plugins out of the box, or you can write your own. The big picture selling point of flake8 is that it's a substrate that other plugins can build on. Yeah. And, personally, I've been using the we make Python style guide
[00:13:30] Unknown:
system, I guess, is 1 way to call it, which builds on top of flake 8 and brings together a lot of these different plugins to be sort of a drop in here, all the things that you need to be worried about and all of the things that you've done wrong with your code so far and how why you should fix them. Yeah. Builds on top of Flakade is a bit of an understatement for Wemade. They're they're the biggest, as far as I understand, the biggest collection of Fleek8 plugins in the ecosystem right now. Yeah. They're definitely very opinionated, which can be valuable because if particularly as a new developer, if you don't know enough to have opinions of your own, you could just drop in, and they've got plenty to spare.
[00:14:04] Unknown:
Oh, I realized that I didn't address 1 of your questions, which is why would somebody who doesn't have any linting get started with this? But I think the real selling point is that a lot of these static analysis tools can identify problems before there are problems. You can know that you missed an import or that, you know, a particular branch has has code that's dead or, like, you have extra imports or, like, you're dealing with global variables in a weird way. Like, a lot of these static analysis tools can tell you about problems before, you know, there are 500 on your website or, like, you shipped a broken app to a consumer or things like that, and they give you more confidence that what you're writing is correct. Yeah. And to give an example of where I wished there was some linting on a project that I had to work with recently, there was a case of
[00:14:50] Unknown:
the variable names in a 4 loop shadowing a variable name higher up in the function that then got returned as the output of the function so that the return value was not even close to what you were expecting. I've definitely made this mistake so many times. Yeah. Which would have gotten picked up by something like flake 8, but because there was no linting on the repository at all, I had to do my own debugging and analysis and sort of follow the code paths manually. So just a a call out to people, add linting to your projects.
[00:15:18] Unknown:
So I stream programming on Twitch several times a week, and I was adding a feature and I had exactly the same bug. And if I would have just run my pie at least once, it would have told me I was reassigning a variable with a different type, and I would have, you know, fixed the bug almost instantly. But sometimes even I'm not fast enough to run the NMS tools.
[00:15:36] Unknown:
In terms of people who are sold on the idea, okay, I need to add linting to my repository. This is a useful tool. This will help me either on my individual projects or my team projects. What are some of the barriers to entry that they might run into after they've said, okay. I wanna add flake8 or I wanna add flake8 with these other plug ins to my project. You know, what might cause them to second guess that decision?
[00:16:00] Unknown:
Yeah. So enabling a linter for the first time is an extremely daunting task, especially if you haven't been doing it before because taking code that doesn't adhere to a particular guideline and making it adhere to a guideline can be very difficult. My recommendation when doing this for the first time, especially if it's, you know, your absolute first saluting experience with the code base is to start small. Let's take flake8 as an example. I would start by enabling flake8 and enabling it on either 1 file or 1 subset of files and getting those to pass. I would also strongly encourage using some of those formatter tools that I mentioned earlier to make this process easier. You know, set up an auto formatter at the same time so that it can just automatically format your code to adhere to the particular style guide. Set up an import sorter, like, use tools like autopepaid or autoflate to remove unused imports or fix up your white space to a place where it's good. But, yeah, the first step is, like, you know, get it enabled somewhere such so you know what you're getting into.
Another thing that you can do is opt out of almost all the flags and turn on 1 flag at a time. So Slake 8 allows you to enable or disable all of the error codes, and you can start with all of them disabled and then opt into 1 at a time and fix those in your particular code base. Or I would recommend doing white space based fixes first. That way, you have some confidence in, you know, that they're not actually changing code, and you can perform those changes safely. And roll those out gradually and be very careful or be as careful as you would be when developing the software normally. Yeah. So the TLDR is, like, you know, enable it piecemeal and
[00:17:35] Unknown:
have code formatters help you out along the way. There are also some tools out there as well that have the capability of adding, like, a baseline set where you can say, I know I have all these problems in my project. These are all the locations. So ignore all of these things until I change the code where those problems live, and then tell me about everything that I've done wrong.
[00:17:55] Unknown:
Mhmm. Yeah. And Flakyant has a built in option that helps you with this too. Per file ignores setting, you can set that up. There's also I've been meaning to open source this tool. I've wrote it once while I was at Yelp, but, unfortunately, it was lost to closed source. But the idea behind it is you pointed out a code base that doesn't have linting yet, and it will add no QA comments to all the lines that are violated, and then you can slowly burn those down over time. I also I created a tool called Get Code Debt. The idea behind it is you give it patterns to match against diffs, and you can graph over time, you know, those patterns in your code base. You might write a no QA rule for that tool and then graph no QA over time and kinda get graph driven development out of it. That's pretty cool.
[00:18:39] Unknown:
Digging deeper into flake 8 itself, can you talk through the overall system design and implementation of the flake 8 project and some of the ways that the design and goals have changed or evolved over time, particularly as the Python language and ecosystem itself has grown and evolved, thinking in terms of things like the, you know, pyproject.toml, new syntax capabilities for the language, the new peg parser for the language itself, and just some of the ways that that plays into your role as a maintainer of a tool that is trying to work so closely with the language.
[00:19:16] Unknown:
So kind of funny you've actually touched on a bit of a tough point in Flakgate right now, which is the push to simplify flake8's configuration such that something like pyproject.toml could be considered. Unfortunately, right now, the configuration story for flake8 is not so great. It's a little bit tangled internally, and I plan to fix that at some point. But until that's fixed, it's really hard for me to add support for yet another standard without really delivering value from it. So pyproject. Toml is early on hold in flake. But, yeah, let's talk about the architecture of flake. I guess the main design of it is that plugins are registered through setup tools entry points, so a project would basically put a little bit in its setup.py or setup.cfg or PyBrowser.toml or whatever config file it uses to supply the setup tools metadata that says, you know, this is a module that provides a Flak8 plugin, so make sure to, you know, import that when Flak8 starts. So it'll load all those plugins, load all the configuration from those plugins, set up command line options, etcetera.
Leave the next thing that it does is it will discover all the files that it should run on. So this will either be positional arguments on the command line or it'll recurse through the directories that are given. Or if you don't give a directory, it will recurse through the that are given, or if you don't give a directory, it will recurse through the current directory. From that, it filters the files based on the filtering settings that you have in your configuration, and then the fun part starts where if you're in multiprocessing mode, which I actually need to fix that as well because that doesn't really work on Windows, and it no longer works in macOS because fork is amazing and not great on macOS.
And so let's talk about the multipressing mode because I think that's the more interesting mode, and it's more likely to continue in the future after I rearchitect how plugins are set up. But, basically, we'll spin up a bunch of multiprocessing workers, feed those files off to multiprocessing workers, which will parse the AST, tokenize the source, and then pass either the AST tree or the individual tokens or token lines or there's several different modes for the non AST versions, and we'll pass those off to functions that plug in runs, and the plug in will produce error messages, and then those will eventually be joined back up into the main process and then report it to the user.
Now I talked a little bit about, like, how the multiprocessing needs to change a little bit. So a lot of it right now depends on plug in state that's not multiprocessing safe on, you know, non fork platforms, so things like class state or global variables are not forwarded along to other processes. I'm going to be building a new at some point, a new plug in architecture, which serializes the actual plug in state and deserializes it on the other end. So you can have a more consistent experience even on Mac OS and Windows, which traditionally don't have fork based multiprocessing.
How do plugins go about implementing or plugging into this ecosystem? So beyond the set of tools, metadata that I talked about before, a plugin tells flake 8 which parameters it needs, and these are just conventionally by name. So if you have a parameter called tree, you'll receive the AST parse tree from flake 8. If you have a parameter called tokens, you'll get the tokens. If you have I think it's, like, logical line and physical line, you'll get, you know, source lines. You can also get token if that's what you're looking into, but you you kind of segment into 2 different types of parsers. 1 would be your AST based parser, which you can use standard library tools to traverse AST, or you can write your own, or you have the token based parsers, which are often either line by line or token by token.
And so for 2 very popular ones, like PyFlakes is implemented using the AST based traverser and Pycode style is implemented using the kind of GAN or line by line approaches.
[00:22:58] Unknown:
And I know that there are some cases too where the AST is too lossy because you might lose comments in the source that you need to be able to understand to be able to say, okay. You know, I need to keep this comment if I'm doing some restructuring, or I need to know there might be cases where the comment actually has some tactic value because of some weird thing that you're doing with your code, or you need to be able to understand, you know, is there some useful information in this comment that will change the way that I interpret whether or not this is a violation of this code style, things like that. Or, you know, maybe I need to do some validation on the content of the comment itself to say, you know, this is a to do comment. It needs to have this structure. There needs to be a time stamp, things like that. In a way, I think if I had to ask for 1 thing from Python,
[00:23:45] Unknown:
just 1 thing, would be a standard library implementation of a concrete syntax tree instead of an abstract syntax tree because that would give you access to all of these comments and other stuff. There are a number of implementations of concrete syntax trees in the Python ecosystem. The constant problem with all of them is they are always somewhat out of date with the actual parsing of Python. A lot of them are either based on lib 2 to 3 or on forks of lib 2 to 3 and, you know, can't necessarily keep up with all of the syntax changes that happen in Python, things like, you know, f strings, assignment expressions, and and now the peg parser because almost all of them are based on l o 1. And so, you know, with a peg parser, you don't necessarily have new syntax, but we do have new syntax in Python, like the multi width statement or the match statement, which prevent those old LL 1 based CST parsers from properly, you know, parsing source.
So fortunately or unfortunately or we don't I guess it's entirely unfortunately. There's no fortunate about it. With the new you know, match statement and peg parser stuff, a lot of these l 1 based tools will either need a rewrite or will sadly fall by the wayside. It would be really nice if the standard library provided a CST out of the box because you could build a lot more powerful tooling on top of it and have a better understanding of the source code. But for now, if you need comments, you'll have to resort to the token based parsers instead of using the AST based parsers.
Although in some cases, so I've run a lot of tools which kinda do a hybrid approach where they take the EST and the tokenization and kinda cross reference the 2 against each other so you can get, like, that rich information from comments and, you know, white space and all of the other stuff that you lose in an abstract syntax tree. You can kinda cross reference that against the AST and make more informed decisions about Lint errors or code formatting changes.
[00:25:41] Unknown:
We've all been asked to help with an ad hoc request for data by the sales and marketing team. Then it becomes a critical report that they need updated every week or every day. Then what do you do? Send a CSV file via email? Write some Python scripts to automate it? But what about incremental sync, API quotas, error handling, and all of the other details that eat up your time? Today, there is a better way. With Census, just write SQL or plug in your dbt models and start syncing your cloud data warehouse to SaaS applications like Salesforce, Marketo, HubSpot, and many more. Go to python podcast.com/census today to get a free 14 day trial and make your life a lot easier.
For people who are building plugins on top of flake 8 or they have an existing linter that they want to integrate into the flake8 runtime, what are some of the limitations of flake8 as a platform for being able to evaluate and report on code style violations? Yeah. So I think the biggest limitation is that well
[00:26:43] Unknown:
well, this is 1 that people ask for a lot, and I I don't think flake 8 fits well into this space is reformatting code. Flake 8 is almost entirely just a checker and isn't going to it doesn't have places to fit in code formatting, so that's not really 1 of its goals. Another thing that people ask for a lot that's kind of a limitation is that richer type information that I talked about before or that richer dynamic analysis. Flak8 is essentially gonna hand you information about a single source file and not any other remaining or peripheral context from there. You basically get your source file, it's AST, and it's tokenization, and that's it. So if you're trying to pull out, like, a typing based tool, Flakgate is not really a great fit for that either, or a dynamic analysis tool. You're you're not really gonna be able to see much beyond your current module.
That said, you can do some clever stuff with AST to get some more of that information. So if you do happen to have your dependencies available, you could write specialized code in your own flaky plug in that reaches out and looks at Sys modules and looks at your site packages and figures out, okay. This function has these parameters, etcetera, etcetera. As for integrating existing tools, I mean, a lot of them are already integrated, so there's not really that much work to do. The tricky part is deciding which of the settings is going to handle and which are the settings the underlying tool is going to handle. So a lot of tools have their own way to include or exclude files or include or exclude Blint rules or or that sort of thing. And Flake 8 works best when it handles all of those. I'll give you an example from a recent tool that I integrated or that fixed an integration with actually, a contributor fixed the integration, but I was involved because I also maintain it. PyDoc style, which is a tool that validates docstrings, there is a flake8 plugin called flake8 docstrings.
I always get these all confused. I think that's right. The PyDoc style has its own idea of no QA comments despite, you know, Flak8 kind of canonically owning no QA as their comment system, but PyDocstyle has their own slightly incompatible implementation of NoQA comments, and there was a bug where there's an option to flake it to disable Node QA, and it wasn't working because this underlying tool's Node QA implementation was firing. Mean, so a setting was added to PyDoc style to disable no QA handling in PyDoc style such that Flake8 could do the correct behavior. I will not correct behavior, but the different behavior.
But we'll leave correctness to the eye of the beholder.
[00:29:15] Unknown:
Fair enough. And so Playgate has been around for a number of years, and there are a pretty wide range of plugins that have been built on top of it. And I'm wondering what you see as the motivating factors that have led to sort of the widespread usage of Flake8 and the wide number of
[00:29:37] Unknown:
plug ins and use cases that it has been bent to? I don't specifically know why it was adopted, but I can posit some guesses as to that. I think the reason that Flaket has seen such a wide adoption is because the baseline is pretty good. It gives you a good, you know, relatively low false positive rate and, you know, catches a lot of common mistakes at the same time because the base tools that it's based on have those design goals in mind. Like, PyFlakes, you know, prioritizes, you know, avoiding false positives and in, I guess, also avoiding false negatives in the same or I guess it it prioritizes eliminating false positives over having false negatives, so it tries to be not a tool that you turn on and you're like, oh, well, the winter is wrong here or like, oh, well, this, you know, this is actually a mistake of the winter itself. It tries to be very good about if it's going to tell you something's wrong, it's probably wrong for the baseline tool. I think beyond that, the adoption has also been good because it's a really easy plug in substrate. Like, if you have an AST parser, really easy to plug in. If you have a tokenizing parser or a checker, really easy to plug in, and it's also really easy to add your own custom roles to that. You have a domain specific requirement.
You could write a Playgate plugin for it, and I think that has helped a lot with its adoption. But also, I think in some ways, just like adoption is a little bit cyclical in Python. Like, if someone's using a tool and other people notice someone else using that tool, like, they might use that tool as well. And then so, like, these things kind of snowball, but I don't know. I think it's good. So I assume that's why people are also using it because it it works.
[00:31:17] Unknown:
I think I think the sort of extensibility factor is definitely something that something that has led to the broadening of its use and its ecosystem. What's already there. Then it's much simpler than having to bring in a whole new tool or write a whole new tool from scratch. And so to that point, what is the process of building a new plugin on top of flake 8 where you say, I've got this syntax that I need to be able to enforce? So, you know, 1 example that I've looked at that I've considered actually going down the road of writing my own plugin for is so I use Daxter, which is a data workflow framework. And 1 of the pieces of syntax that it has is that you can, in the decorator, say, these are the types of inputs and these are the types of outputs and the names. And then, you know, at the bottom of the function, you need to yield an object that has that same name as 1 of the inputs. And so in order to reduce the necessity of jumping to the beginning and end of functions all the time, I wanna be able to say, okay. Does this name in the output match 1 of the names that I specified in the decorator?
[00:32:27] Unknown:
So sort of given that concrete example, what would be involved in actually writing a plug in to be able to enforce those checks? Yeah. So I actually made a video about this that maybe you can link in the description, but I'll give you a short TLDR about how I usually approach this. And, again, like, why I would write a plugin over building a full tool is is because Flake8 provides a whole bunch of stuff on top of it that I just don't have to think about. I don't have to think about file names. I don't have to think about inclusion exclusion or disabling lints or any of that other stuff. Like, it just kinda handles that for me. But as for writing a plug in, what I usually start with is to write an AST node visitor, which is part of the standard library AST package, and it basically gives you a recursive descent AST traversal.
From that, you can write a set of code in each of the visit functions, which finds your particular code structure that you're looking for. So in your case, you're probably looking for visit function def to find the decorator for your particular thing, and you're probably looking for visit return to find the return inside that function def. You can also write, you know, some amount of state in your AST traverser to say, am I in a function that's decorated, and then have your visit return, you know, do what's necessary based on that. Once you have an AST traverser, you can write a little bit of glue code to make it work well with itself, and this usually involves having a class that has, I believe, its plug in name, plug in version, and then an initialization function that takes tree and another function, which I believe yields the results.
So a plugin class has to have a name, it has to have a version, it has to have an initialization function, which takes the things it needs to parse, so tree or tokens or other stuff like that, and it needs to have a run function, which is a generator and yields the error messages. So in your case, you would construct your AST traversal class and then run it over the parsed tree and yield out each of your errors that are response to that AST parsing. That's for AST based plugins. There's also token based plugins. Token based plugins are a little bit simpler. You just write a function which takes in the particular token stream or line or other stuff like that, and you register that with setup tools, and I believe you attach some attributes to it. In my experience, the token based checkers are not as common,
[00:34:53] Unknown:
although Pycode style is a good example of those. Yeah. It definitely seems that the use case for token based checkers is going to be more limited in terms of the statefulness of the check where you're just going to say, is this thing in isolation on this specific line matching my expectation rather than the example that we were just discussing where I need to know based on the information at the top of this function, does it match my expectations given the end of this function where I need to be able to maintain state across those 2 boundaries?
[00:35:23] Unknown:
Mhmm. Yeah. The token based ones don't have as much state. Although, you can build token based checkers with state because you can receive the entire token sequence as 1 of those initialization parameters on your plugin. That's actually relatively new. That wasn't a thing until, actually, PyFlakes needed it to do some typing analysis on type comments.
[00:35:44] Unknown:
And so as a maintainer of flake8, given the fact that it is such a widely used tool and has so many different plug ins and ways that people are using it, what are some of the challenges that that poses for you specifically?
[00:35:58] Unknown:
Yeah. I think the hardest part for me is getting a large enough corpus of code to run it against to validate any change that happens, especially when dealing with PyFlakes and Pycode style. They have really great test suites, but they can't cover every possible use case that anyone has, And identifying new use cases, adding test cases for those, and validating those is kind of difficult. I guess that's kind of a problem of any in Linter or CodeFire Runner. Like, you need to have confidence in your changes to either new rules or existing rules don't break too many people. As for Flakgate itself, I think it's relatively isolated from the changes in the underlying tools, but it does have a certain amount of complexity that it takes on in managing know, managing the token streams, managing the line handling.
There's often changes in the AST that Flakgate has to make slight adjustments for, for instance, like, you know, deciding whether a docstring is ignored or not because it's a multiline token, and it reports its line number differently based on what Python version you're in. Or dealing with decorators, which I believe it was 38 changed their changed their position from being the top of the function to not the top of the function or their actual real position. So flake 8 occasionally has to make changes for those. I think the biggest thing or like the places where flake 8 has had the most breakage is in these ever so slight changes to how the file recursion or inclusion or exclusion works, especially in relation to the configuration file.
Then like I said before, the config file situation is more complicated than it needs to be and pretty hard to reason about currently, and I actually kind of want to rip it out and start over from that that part. I know ripping out and starting over presents a whole different set of problems because you have to satisfy all of the existing use cases or at least enough of the existing use cases that you don't orphan your users on an old version or prevent them from upgrading or cause them to no longer use the tool. You're always balancing between adding new features, keeping your existing users happy, and fixing things as the language and tools evolve.
[00:38:03] Unknown:
To that point, if you were to start over today with a clean slate, what are some of the architectural or design changes that you would make or changes in the overall goals and priorities of the project?
[00:38:18] Unknown:
I think probably the 1 well, if I were just basing this on the confusion that I see in the bug reports, I would probably start by making the configuration situation much simpler. As it is right now, you can have multiple config files in a bunch of different locations, and they have a cascading override of each other, and it's really confusing. It's really hard for people to you know, exactly what's going on. I think the first thing that I would do is simplify that down to just a single config file and, like, don't give you as many choices about where that config file can live. Like, you know, either it's the root of your project or the root of a folder and, like, everything upwards is your project.
Centered around a project instead of right now, it just does directory traversal. I guess, like, this also ties into how you would work on it in a version controlled repository. Maybe have better visual or better insight into, oh, I'm in a Git repository. I probably don't need to link to virtual environments or have more reasonable defaults about deciding when to traverse or not. I think the biggest 1 for me would be well, these are things that I plan to do anyway. So 1 of them is, you know, simplify the configuration file setup, and the other is make plugins more explicit about their individual settings and such and pass those along in a same way so that multiprocessing can continue to work on into the future.
[00:39:36] Unknown:
In terms of the ways that you've seen Flak8 used or the extensions that have been built on top of it, what are some of the most interesting or innovative or unexpected systems that you've seen built with flake8 and its overall plug in capabilities?
[00:39:48] Unknown:
I mean, there's a few unexpected and bad ways. So I've seen, in my opinion, a lot of those other buckets that I described earlier, so, like, pipe checking and formatting, I don't think they fit well into Flakyat's, you know, goals, and so I've seen plugins that happen to integrate those, and they kind of work. As for, like, some of the things that, you know, when I see a flake 8 plug in, I'm like, that's a really good idea. I think most of them are around, like, domain specific flake 8 plugins. I gave the example. It's a localization plugin that I talked about before. Like, that 1 was really useful for making sure that, you know, we don't perform the wrong thing and translate your app incorrectly.
I think, like, anytime you can take, like, an incident response situation where you're like, we made this mistake, take that mistake, encode it as a static analysis implementation tool thing, and then prevent that problem going forward. I think that's the coolest thing that I've seen from Flak8 is using it as a substrate to build plugins that prevent common domain specific problems.
[00:40:52] Unknown:
In your experience of working on and with Flake8, what has been the most interesting or unexpected or challenging lessons that you've learned in that process?
[00:41:03] Unknown:
I think probably the most well, this this no longer surprises me, but when I first started working on Flak8 or other related Winters and Code Promoter tools, I was surprised how often I found bugs in either the parser or the tokenizer of Python or differences in implementation between the C based tokenizer and the pure Python based tokenizer. But also, you know, it surprised me, but in the same way it surprised me at how quickly and how easily those can get fixed. So I actually made, like, a lot of contributions to CPython just to improve a lot of these situations where, you know, the parser is slightly wrong or the tokenizer is slightly wrong or it's slightly different between the different implementations.
I think that's I mean, anytime you're dealing with a wide user base and a somewhat complicated tool, you're gonna run into these edge cases that are going to surprise you, and almost all of these have been around like, oh, I didn't know you could do that with the syntax or, oh, well, this is kind of broken. But, yeah, I think that's probably the biggest takeaway from there. And so for people who are evaluating
[00:42:09] Unknown:
what types of tools to bring into their project and how to enforce style and correctness within their systems. What are the cases where flake 8 is the wrong choice?
[00:42:19] Unknown:
I think maybe the only place where I would say flake 8 is not a good choice for Python code is for Python code written in notebooks. And you can still do some static analysis, some notebooks, and some linting and code formatting, and there are tools for this, but you necessarily have a different style of writing code for notebooks. Like, you're less concerned with, you know, variables assigned that you're never using later. Like, you might be assigning it just so that it produces the particular output you want in a cell. You're not really designing or architecting your code in a way that's meant to be reusable. It's 1 off, throw away code, so its quality can be less than, you know, production quality code.
Beyond that, I think, like, let me clear here. I think you should have static analysis on all the code you write, even code in notebooks. I just think that Flakgate doesn't specifically solve that goal. I would say, like, lint everything. And there are some things you can't lint, like, you know, Ginger templated YAML. You have no idea whether it's Ginger or whether it's YAML, and you can't really know whether it works until you run it. But
[00:43:19] Unknown:
That was, yeah, that was very poignant. Way too much of that.
[00:43:26] Unknown:
It's too close to home? Yes. Very much so. Is it SaltStack or In The Bolt for you?
[00:43:31] Unknown:
Yes. Oh, no. It's both. The caveat that you put in there about if you're working with Python code is also something that's interesting and probably merits its own whole conversation is the idea of multi language linting and multi language projects and sort of code quality across those boundaries.
[00:43:49] Unknown:
But I know that's sort of out of scope of this conversation. But No. For sure. Well, my answer to that is always use Pre commit because it supports a whole bunch of different programming languages and
[00:43:58] Unknown:
is really easy to plug in. As you continue to work on Flak8 and continue to be the maintainer, I guess, first off, what are some of the ways that the community can help you in that effort? Are there any specific sort of areas of contribution that you're looking for help with? And in terms of the direction that you're taking, you know, how can people help you get to your
[00:44:22] Unknown:
desired future state of flake 8, whatever that might be? Yeah. I think the biggest things for me is, you know, contribute plugins, work on improving the base case of PyetFlakes and Pycode style, especially submitting new bug reports when things change in unexpected ways with code samples. And the more code samples and test cases that we can get in all those tools, the higher quality they will be moving forward and less likely that we are to contribute regressions. I think from Flak8 itself, there's gonna be some large changes coming soon where I'm going to ask feedback from the community and, like, if people can reply to those and let me know whether some of the changes are gonna be welcome or not. Like, that'll be super helpful, especially around, like, configuration changing and plug in architecture.
Those are gonna be the big problematic hard things to change moving forward. But, yeah, I think, like, the most impactful thing that anyone can do is work on a plug in, like, make make those plug ins work well, then make that experience good for anyone integrating those.
[00:45:23] Unknown:
Are there any other aspects of the flake 8 project or code quality and linting and sort of overall being a developer and your work as a maintainer that we didn't discuss yet that you'd like to cover before we close out the show?
[00:45:36] Unknown:
I mean, I'm always trying to plug pre commit, so I think you should set it up, set up a linter, set up a code parameter. In Python, you probably want, like, you know, code parameter, import sorter, and checker, and, you know, probably the most common combination of those is black, isort, and flak8. For me, it's auto pip 8, reorderpipe and imports, and flak8, but those 4 core tools, because PreQ Med is 1 also, are going to take you a long way to having quality code and linters and code formatters can go a long way to eliminating those nitpick discussions that you have in code reviews and shifting those towards automated tooling that does all this for you so you can focus on architectural changes.
[00:46:15] Unknown:
Absolutely. And for people who are new to pre commit, it's worth noting that you should add your code formatter and your import sorter before your linter so that it doesn't report on errors that are going to get fixed automatically.
[00:46:26] Unknown:
Exactly.
[00:46:28] Unknown:
Alright. Well, for anybody who wants to get in touch with you and follow along with the work that you're doing, I'll have you add your preferred contact information to the show notes. And with that, I'll move us into the picks. And this week, I'm going to choose the book 7 eves by Neal Stephenson. I started reading that recently, and it's a very interesting premise, a bit anxiety inducing, but it's, you know, well written and well worth to read. You know, the premise being that the moon just all of a sudden breaks up into multiple fragments for some unknown reason, and then they they realize that all of those fragments are going to rain down on the Earth and eventually cook the planet. And so they have to try and figure out how they can evacuate enough people into space to be able to survive for many 1000 of years to until the Earth becomes rehabilitable and come back down. I'm still in the phase of where they're trying to get some number of people up into space, but it's very interesting and well written book. So something worth reading if you're looking for something to pass the time. And so with that, I'll pass it to you, Anthony. Do you have any picks this week? That's wild.
[00:47:28] Unknown:
I used to really like everything bagels, but recently, I've taken a disliking to cardamom, the spice, and everything bagel without cardamom is, like, the perfect bagel or anise or whatever it's called. It has 3 different names. I haven't been consuming a lot of media recently because of the pandemic. I feel like I've mostly just been working on my start up, you know, streaming, programming on Twitch, and working on open source, and not really spending much time on other things. I guess I can talk about my startup. My startup is called Precommit CI. It is a CI system based around Precommit, so it takes all of the ideals of that open source tool and then makes it really easy to integrate with
[00:48:06] Unknown:
your CI system. So the TLDR is it runs precommit on your pull requests, and if it has code formatting changes, it'll autofix those on your pull requests. It's also much faster than the equivalent free services that you would write and has distributed caching, so it's super super speedy to run your clincers and code formats. And it's free for open source, which is cool. Well, thank you very much for taking the time today to join me and discuss the work that you've been doing with Flake 8. I appreciate all of the time and effort as a user of Flake 8 has helped me greatly in many of my projects. So thank you for all of the time and effort you've put into that and all of your other other projects, and I hope you enjoy the rest of your day. Yeah. Thank you for having me on, and have a good 1. Thank you for listening. Don't forget to check out our other show, the Data Engineering Podcast at dataengineeringpodcast.com for the latest on modern data management.
And visit the site of pythonpodcast.com to subscribe to the show, sign up for the mailing list, and read the show notes. And if you've learned something or tried out a project from the show, then tell us about it. Email host@podcastinit.com with your story. To help other people find the show, please leave a review on Itunes and tell your friends and coworkers.
Introduction and Guest Introduction
Anthony's Journey with Python
Becoming the Core Maintainer of Flake8
Overview of Python Code Quality Tools
Motivations for Using Flake8
System Design and Implementation of Flake8
Limitations and Challenges of Flake8
Building Plugins for Flake8
Maintaining Flake8
Future Directions and Community Involvement
Lessons Learned and Use Cases
Final Thoughts and Picks