-
-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch backend to libCST #124
Comments
Leaving flake8 as a backend does not really seem worth it at the moment, it's added some headaches and complications at times - but we've managed to work around pretty much all of them so I don't currently see any particular upside in ditching it - unless new versions come out that break things (which isn't that unlikely tbh). |
OK, I like the idea of an incremental migration pathway so let's stick with For autofixes, it'd be nice to hook that from |
I'll have to have a look at hooking in codemods, feels like I'm realizing now that I don't know how flake8 will react to having the file changed under it - probably badly. A temporary solution could be to write the modified file to |
I went through the list of checks and categorized them as follows. Can be autofixed
Cannot be autofixed
user-configured
I think many of these can be fixed, but needs a long list of what to replace with
Requires a bunch of logicI think I can chuck in a bunch of
|
I was thinking that we'd have the |
Re: your notes on specific rules, mostly looks great. Few edits:
|
Ah, but not if the nursery is passed |
Working on libcst now and will probably push something up today/tomorrow.
I thought this sounded good, but you're going to end up with two trees, the unmodified and the transformed one, which at the very least are going to desync in line numbers - but possibly in even bigger ways. With autofixing turned on, you want the line numbers from the transformed file in the output, with it off you'll want line numbers from the original tree. |
To get started on autofixing, I think I might as well get it running as an independent plugin at the same time so it's usable. Looked through how various linters are run, and the one in shed looked simple enough to get started with. So any comments on that before I get started copying much of the implementation of it? https://github.com/Zac-HD/shed/blob/master/src/shed/_cli.py I'll be adding a couple flags only valid when run independently (which will error out and tell the user to run it without flake8 if passed with that), but try to otherwise keep a very similar interface regardless of if it's run with or without flake8. |
That sounds good to me! Obviously the already-converted checks are the best place to start autofixing, but then missing/redundant checkpoints would be highest priority as they're a pita to do manually. |
Got started on converting 91X today~ |
Some solid progress, eval_file 910 only has a single incorrect error at the moment, and I'm steadily chipping away at the ones in 911. Although it is kinda hilarious to see this table
Merely 32 errors too many on line 634, nbd 😁 |
I feel like we're slowly closing in on warranting a reconsider a name change for this project - it's soon not dependent on flake8 (although will continue being possible to run as a plugin), and while trio is the main focus, we've added support for anyio as well. So both parts of flake8-trio are kind of misleading at this point 😅 |
"It's called Yeah, seems like a new name might be in order! Maybe something mentioning structured concurrency?
|
if len(all_filenames) > 4:
# If we're formatting more than a few files, the improved throughput
# of a process pool probably covers the startup cost.
try:
with multiprocessing.Pool() as pool:
for error_msg in pool.imap_unordered(rewrite, all_filenames):
if isinstance(error_msg, str):
print(error_msg) # noqa
return
except BlockingIOError as err: # pragma: no cover
# This can occur when `os.fork()` fails due to limited available
# memory or number-of-processes. In this case, we fall back to
# the slow path; and reprocess whatever we've already done for
# simplicity. See https://stackoverflow.com/q/44534288/
print(f"Error: {err!r}\n Falling back to serial mode.") # noqa |
Haha, scuba is fun. "scry" is also taken... But there's a long list of words starting with Both
and
which have some pretty fun self-deprecating properties 😁 Or something-something "the checker might point out some common errors, but as a true sciolist it only has superficial knowledge with no deep understanding and should not be fully relied on" |
updated OP with full task list (as of now) |
added 5 more tasks:
|
We're building a dedicated auto-fixing linter on top of LibCST called Fixit, with a plan for a stable release in the next month or so. Would you be interested in considering Fixit as a base for this work, rather than reimplementing your own linting and autofixes from scratch? |
Oh interesting, using that as a middle layer to libcst could help solve some of the pain points with modularity, shared state, configuration, and other stuff. This plugin is in a coasting phase atm though, and we probably wouldn't see much gain from overhauling the infrastructure - but if performance or something other becomes a pain point it might warrant further consideration. I'm not seeing any way of hooking in plugins in the documentation though, or are you suggesting something like moving rules over to the fixit repo? The latter I don't think is terribly relevant with how niche/opinionated (/overcomplicated 😁 ) many of the rules are in this. Though you're very welcome to crib rules if you want :) The libcst autofixers in https://github.com/Zac-HD/shed might be even more relevant |
The general idea is that Fixit makes writing and distributing rules as simple as possible. Rules don't need to define "plugins" to be used — the user simply adds the module/package name containing the rules to their project config, and Fixit will automatically find and import them, assuming they're available in the environment or locally in the project being linted. For this project, assuming the current name is maintained, and all of the rules were defined in the top-level
Fixit would then import the The Fixit configuration guide has more details and examples. |
Cool! As I said it's not really worth the effort to reconsider switching the backend at this point, but I'll keep Fixit & friends in mind for the future~ ✨ |
with ruff starting to implement these rules they chose the |
Maybe @cooperlees and I should formally recommend stealing |
We should be able to re-code these rules if you choose to change the prefix. (We also have the |
Maybe you just take flake8-async? Happy to transfer away ... I don't have time for it etc. etc. |
Sure, transfer it to my personal account? I'll make a last release to deprecate it in favour of the project currently known as flake8-trio, and then we can use the ASYNC key here and for the corresponding ruff rules. |
We definitely need to rename this then imo, could maybe even take over the flake8-async moniker - though I should make sure a decent subset of rules work with asyncio & give good error messages. |
Ok, initiated ... |
And, transfer accepted! Thanks, Cooper :-)
It makes sense to ensure that the current rules don't give nonsense results on asyncio code, though I probably wouldn't bother writing any asyncio-specific rules... if you want your async code to be correct, use anyio or trio! |
Oh yeah, that's the extent I was thinking of.
I double checked now as well that we cover all the methods in flake8-async too, and we do. The error codes are different and they're split slightly different - but otherwise current flake8-trio is a strict superset. Then we're settling on flake8-async as the name for the project as well? I was worried about some confusion of thinking async => asyncio, but given that |
OK, with the transfer complete there are a couple of action items - I've assigned most of them to @jakkdl and those which need specific permissions to me, but happy to take other contributions too of course!
and then I think we're ready to ping Charlie about ruff codes 🙂 |
after (hopefully correctly) releasing new versions of flake8-trio and flake8-async it's now relevant for @charliermarsh that you might want to recode rules. |
Am I correct that we should "remove" flake8-trio and redirect the rules to codes under flake8-async? |
Yes, that's what we've done here and I'd encourage ruff to do so as well. |
Moving from #70
what does the UX look like if we're not leaning on flake8 for that? I presume similar, so how much work is that to add?
Quickly looked at whether the flake8 interface could still work - and it's possible to still use flake8 without much problem. You can indicate that you want the lines of a file and then that can be dumped into
libcst.parse_module()
. Flake8 does require plugins to accept one oftree
,logical_line
orphysical_line
as parameter in__init__
for it to classify the plugin and run it at the correct time, so we'd have to still accept thetree
parameter - but we can just throw it out and construct our own tree from thelines
sent at the same time.Somewhat hackish, but means that the consideration of ditching flake8 is pretty much fully independent.
This even means that the transition can even be gradual - where in the intermediate phase you have both an AST and a libCST tree, and run separate runners for them.
TODO
Standalone functionality
flake8-async
could read config files likeflake8
#189Converted to libCST:
Autofixing
finally:
orexcept BaseException/trio.Cancelled
unless you use a shieldedcancel scope with a timeout.
await
ing it.nursery.start[_soon]
and not passing itself as a parameter can be replaced with a regular async function call.nursery.start_soon
in__aenter__
doesn't wait for the task to begin. Consider replacing withnursery.start
.trio.sleep(0)
with the more suggestivetrio.lowlevel.checkpoint()
.trio.[NonBase]MultiError
, prefer[exceptiongroup.][Base]ExceptionGroup
. Even if Trio still raisesMultiError
for legacy code, it can be caught withBaseExceptionGroup
so it's fully redundant.user-configured
trio200-blocking-calls
for how to configure it.I think many of these can be fixed, but needs a long list of what to replace with
httpx.AsyncClient
.httpx.AsyncClient
. Looks forurllib3
method calls on pool objects, but only matching on the method signature and not the object.await nursery.start(trio.run_process, ...)
.await trio.run_process(...)
.os.*
call in async function, wrap inawait trio.to_thread.run_sync()
.trio.open_file(...)
.trio.wrap_file(...)
.trio.wrap_file()
to get an async file object.os.path
in async functions, prefer usingtrio.Path
objects.Requires a bunch of logic
I think I can chuck in a bunch of
trio.lowlevel.checkpoint()
just before the line that would raise these - but there's going to be a bunch of edge cases that are tricky.higher priority
The text was updated successfully, but these errors were encountered: