-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
(Apologies for the slightly negative tone in this issue -- I'm a big fan of ruff, and think it's a great tool!)
If I have a file foo.py, like so:
isinstance('foo', (int, bytes, dict, set, list, memoryview, str))Running ruff foo.py --select=UP --fix --target-version="py310" on this file will make the following change:
-isinstance('foo', (int, bytes, dict, set, list, memoryview, str))
+isinstance('foo', int | bytes | dict | set | list | memoryview | str)The new code is more verbose. I wouldn't mind that so much, however... if it wasn't also much slower!
>python -m timeit "isinstance('foo', (int, bytes, list, set, dict, str))"
Running PGUpdate|x64 interpreter...
2000000 loops, best of 5: 160 nsec per loop
>python -m timeit "isinstance('foo', int|bytes|list|set|dict|str)"
Running PGUpdate|x64 interpreter...
500000 loops, best of 5: 500 nsec per loop
(These timings are using a ~reasonably fresh PGO-optimised non-debug build of the CPython main branch on Windows.)
The rule can of course be switched off in a config file for ruff. But I'm not sure what advantages this rewrite actually has. The issue that proposed this rule (#2923) doesn't really give any motivations for wanting these isinstance() checks to be rewritten other than "it would be great". I also feel like the comment in #2923 (comment) is overly dismissive of the performance concerns when the writer says that "isinstance() checks... are very unlikely to dominate any real-world workloads". It can absolutely cause real-world issues if an isinstance() check is unexpectedly slow: see python/cpython#74690 (comment) as an example.
My preferred outcome would honestly be if ruff removed this rule. I feel like it's encouraging an anti-pattern, and it means that I can't have select = ["UP"] in my ruff config without also adding ignore = ["UP038"]. If it has to stay, though, I feel like the performance considerations should be loudly called out in the docs -- there's currently no mention of the potential issues here.
Note that this rule doesn't exist in pyupgrade itself, only in ruff: it was specifically rejected for pyupgrade, in part because of the performance issues: asottile/pyupgrade#736.