-
-
Notifications
You must be signed in to change notification settings - Fork 279
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
RuntimeError
when analyzing sys.modules
live object
#2686
Milestone
Comments
I'd be interested in seeing option 3 as a PR. What do you think @jacobtylerwalls? |
I agree, let's see option 3. It may not be so hard to maintain. Thanks for the analysis @aatle! |
Merged
luketainton
pushed a commit
to luketainton/webexmemebot
that referenced
this issue
Mar 9, 2025
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [astroid](https://github.com/pylint-dev/astroid) | project.dependencies | patch | `<=3.3.8` -> `<=3.3.9` | --- ### Release Notes <details> <summary>pylint-dev/astroid (astroid)</summary> ### [`v3.3.9`](https://github.com/pylint-dev/astroid/blob/HEAD/ChangeLog#Whats-New-in-astroid-339) [Compare Source](pylint-dev/astroid@v3.3.8...v3.3.9) \============================ Release date: 2025-03-09 - Fix crash when `sys.modules` contains lazy loader objects during checking. Closes [#​2686](pylint-dev/astroid#2686) Closes [pylint-dev/pylint#8589](pylint-dev/pylint#8589) - Upload release assets to PyPI via Trusted Publishing. Refs [pylint-dev/pylint#10256](pylint-dev/pylint#10256) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTEuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE5MS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL2RlcGVuZGVuY2llcyJdfQ==--> Reviewed-on: https://git.tainton.uk/repos/webexmemebot/pulls/472 Reviewed-by: Luke Tainton <[email protected]> Co-authored-by: Renovate [BOT] <[email protected]> Co-committed-by: Renovate [BOT] <[email protected]>
luketainton
pushed a commit
to luketainton/pypilot
that referenced
this issue
Mar 9, 2025
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [astroid](https://github.com/pylint-dev/astroid) | project.dependencies | patch | `==3.3.8` -> `==3.3.9` | --- ### Release Notes <details> <summary>pylint-dev/astroid (astroid)</summary> ### [`v3.3.9`](https://github.com/pylint-dev/astroid/blob/HEAD/ChangeLog#Whats-New-in-astroid-339) [Compare Source](pylint-dev/astroid@v3.3.8...v3.3.9) \============================ Release date: 2025-03-09 - Fix crash when `sys.modules` contains lazy loader objects during checking. Closes [#​2686](pylint-dev/astroid#2686) Closes [pylint-dev/pylint#8589](pylint-dev/pylint#8589) - Upload release assets to PyPI via Trusted Publishing. Refs [pylint-dev/pylint#10256](pylint-dev/pylint#10256) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4xOTEuNCIsInVwZGF0ZWRJblZlciI6IjM5LjE5MS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL2RlcGVuZGVuY2llcyJdfQ==--> Reviewed-on: https://git.tainton.uk/repos/pypilot/pulls/314 Reviewed-by: Luke Tainton <[email protected]> Co-authored-by: Renovate [BOT] <[email protected]> Co-committed-by: Renovate [BOT] <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This issue details my findings from investigating pylint-dev/pylint#8589 (see that issue for more information and steps to reproduce) after my PR couldn't pass checks.
I found the (quite arcane) cause and some different potential fixes.
Bug
Under rare circumstances, pylint crashes with
RuntimeError: dictionary changed size during iteration
.Cause
For this bug, during pylint checking, one of the modules in
sys.modules
is animportlib.util.LazyLoader
object. These 'lazy modules' only execute their contents the first time an attribute is accessed on the module.The lazy loader module is put in there by any sort of package that needs to be loaded during pylint execution: the package must be installed and importable. One reason why pylint would need to load the package is being included in pylint's
extension-pkg-whitelist
(forpygame-ce
's case).During checking (of any arbitrary code that needs the package to be loaded), pylint analyzes the
sys
module live (live because there is no python source to analyze statically), and builds child nodes for its members, including forsys.modules
dict atastroid.raw_building.InspectBuilder.object_build()
.Since
sys.modules
is a dict, pylint also tries to create nodes for the keys (strings) and values (module objects) atastroid.nodes.node_classes._create_dict_items()
, by iterating over the dict's.items()
and callingconst_factory()
on each key and value. The actual livesys.modules
dict is iterated over, not a copy.Eventually the iteration reaches the
LazyLoader
module object. Insideastroid.nodes.node_classes.const_factory()
, the.__class__
attribute of the lazy module is accessed, either implicitly by theisinstance()
assertion, or explicitly on the next line.Since an attribute was accessed on the lazy module object for the first time, it's contents get executed. If it happens to import modules and packages during it's loading, new modules are put into
sys.modules
dict by the python import system, changing its size.After the lazy module is fully loaded, code execution eventually gets back to where
sys.modules
's.items()
are being iterated. Upon the next iteration, python realizes the dict size has changed and raisesRuntimeError
at that point in the code.Comments
metaflow
,pyjanitor
,pygame-ce
(lazy module branch),django
. From what I see, these all utilize lazy loading andimportlib
.Possible Fixes
sys.modules
dict for live analysis. Technically a crash is still possible though, only addresses the common case.value
insideconst_factory()
. I checked and it is easy: replace twovalue.__class__
withtype(value)
(bypassing attribute access), and useissubclass()
instead ofisinstance()
(becauseisinstance()
uses__class__
) in one assertion, all inconst_factory()
code. Keeps original efficiency, but has side effects: harder to maintain,__class__
is no longer be able to be proxied (possibly a good thing?), and__getattribute__
is no longer invoked by pylint (probably good but is another change in behavior).astroid 3.3.8
The text was updated successfully, but these errors were encountered: