Fix a crash in not-async-context-manager when the manager has no name - #11103
Conversation
visit_asyncwith built the message argument from inferred.name, but an inferred context manager need not have a name -- e.g. async with slice(None) infers to a Slice node -- so pylint crashed with AttributeError instead of emitting not-async-context-manager. Fall back to the inferred type name (slice), matching how other value managers are already reported (int, ...). Closes pylint-dev#11102
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #11103 +/- ##
=======================================
Coverage 96.29% 96.29%
=======================================
Files 178 178
Lines 19737 19743 +6
=======================================
+ Hits 19006 19012 +6
Misses 731 731
🚀 New features to boost your workflow:
|
Pierre-Sassoulas
left a comment
There was a problem hiding this comment.
Thank you for contributing to pylint ! Let's anticipate other similar issue and use domain knowledge to make the guard better.
| async with slice(None): # [not-async-context-manager] | ||
| pass |
There was a problem hiding this comment.
Let's also add a with slice(None):. And move it to the functional test of the checker or checkers that are going to crash when we do :)
There was a problem hiding this comment.
Good call. The synchronous with slice(None): crashes not-context-manager in the same way (astroid raises AttributeInferenceError on the missing .name), so I added a functional case for it in tests/functional/n/not_context_manager.py and fixed that checker too (verified red before the fix, green after).
| # inferred from ``slice(...)``); fall back to its inferred type name. | ||
| inferred_name = ( | ||
| getattr(inferred, "name", None) or inferred.pytype().rsplit(".", 1)[-1] | ||
| ) |
There was a problem hiding this comment.
We can often narrow down the type of the inferred node using the visitor pattern filtering and domain knowledge about python syntax in order to use isinstance check instead of getattr
There was a problem hiding this comment.
Done. I checked what actually reaches this point: every other inferred result is name-bearing (a class/function, or an instance such as the int that astroid models as a named Const for 42); a Slice from slice(...) is the only nameless case. So the guard is now an explicit isinstance(inferred, nodes.Slice) that falls back to the builtin type name, instead of getattr(..., "name", None). Same guard applied to the sync not-context-manager site.
Replace the getattr-based name fallback with an explicit isinstance check on nodes.Slice (the one inferred result reaching these checks without a name; everything else, including the int from a Const, is name-bearing). Apply the same guard to typecheck's not-context-manager check, which crashed identically on 'with slice(None):', and add a functional test case there.
| # ``int`` from ``42``, which astroid models as a named ``Const``). | ||
| inferred_name = ( | ||
| inferred.pytype().rsplit(".", 1)[-1] | ||
| if isinstance(inferred, nodes.Slice) |
There was a problem hiding this comment.
I'm not certain we can be sure that Slice is the only node without a name attribute here, I'd rather have an allow list of node known to work (baseinstance/classdef/functiondef/module etc.)
There was a problem hiding this comment.
Good point, switched to that approach in c0a184e. Both sites now read .name only from an allow list of node types that define it (ClassDef, FunctionDef, Module, bases.BaseInstance) and fall back to the inferred type name otherwise, so any unforeseen nameless node degrades gracefully instead of crashing. The existing functional cases (instances, generators, int, etc.) are all BaseInstance/ClassDef so their messages are unchanged, and slice(...) takes the fallback and still reports slice.
Pierre-Sassoulas
left a comment
There was a problem hiding this comment.
Thank you for checking, let's just invert the logic from disallow list to allow list to avoid future crash if the list is not perfect and let's merge.
Read .name only from ClassDef/FunctionDef/Module/BaseInstance (the nodes known to define it) and fall back to the inferred type name for anything else, instead of special-casing Slice. This avoids relying on Slice being the only nameless inferred node.
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Keeps the typecheck.py allow list identical to the async_checker.py one after the review suggestion added nodes.Lambda there.
|
Mirrored your |
… when the manager has no name (#11107) Fix a crash in not--context-manager when the manager has no name (#11103) visit_asyncwith / visit_with built the message argument from inferred.name, but an inferred context manager need not have a name -- e.g. async with slice(None) infers to a Slice node, so pylint crashed with AttributeError instead of emitting not-async-context-manager. Fall back to the inferred type name (slice), matching how other value managers are already reported (int, ...). Closes #11102 (cherry picked from commit c4f8d9b) Co-authored-by: Vincent Gao <gaobing1230@gmail.com> Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Type of Changes
Description
async with slice(None):crashed pylint:visit_asyncwithbuilds thenot-async-context-managermessage argument frominferred.name, but the inferred context manager isn't guaranteed to have a name.slice(None)infers to aSlicenode, which has noname, so emitting the (otherwise correct) warning raisedAttributeErrorand aborted the run.It now falls back to the inferred type name when there is no
name, soslice(None)reportsAsync context manager 'slice' doesn't implement __aenter__ and __aexit__.— consistent with how the other value managers are already reported (e.g.async with 42→'int'). Among the value nodes that can appear here onlySlicelacks aname(Const/List/Tuple/Dictall expose their type name), so this covers the reported case.Added the
slice(None)case to the existingnot_async_context_managerfunctional test (crashes onmain, passes here).Closes #11102