Why can't Idle import from .py files created after it was launched?
Question:
I can’t explain this phenomena, other than to assume that Idle somehow works off of a snapshot
of the filesystem, taken at launch-time.
Repro steps:
- create a
myLib.py
file with (e.g.):
#!/usr/bin/env python3
pre_launch_str = "Pre-launch!"
# post_launch_str = "Post-launch!"
- launch Idle (from the containing folder)
from myLib import pre_launch_str
works as expected: the string is imported/usable
- Keep Idle running/open
- [from another application/terminal] modify
myLib.py
to include a new object (e.g.) post_launch_str
from myLib import post_launch_str
will throw an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name 'post_launch_str' from 'myLib' (/home/myUser/myLib.py)```
Anyone know what the cause of this is?
Linux (zsh) + Python 3.10 inthe example above, but I've noticed this long ago (~Python3.5 and on MacOS too)
Answers:
This has nothing to do with IDLE. When you’ve already put a module into scope (regardless of which names you imported from it), Python will never reload that module. It assumes the module’s code has not changed and will use the in-memory version.
If you’re planning to modify the module live during development, you’ll need to use importlib
to reload it. You need a reference to the module itself. If you imported the module name as import myLib
, then myLib
will do. Otherwise, take a class or function that you imported and get its __module__
(so pre_launch_str.__module__
in your example). Then take that and call reload
on it.
from importlib import reload
reload(pre_launch_str.__module__)
tl;dr: importlib.reload(myLib)
does allow you to grab up-to-date content from myLib.py, but only if you imported the whole module with import myLib
.
Based on Silvio’s answer, I did some playing around:
- when using
import X
, it is possible to use reload(X)
(note the lack of quotes around X
)
- when using
from X import Y
reload(Y.__module__)
will throw: TypeError: reload() argument must be a module
reload(X)
will throw: NameError: name 'X' is not defined
As Silvio noted, class
and function
objects have the .__module__
attribute (while str
, list
, dict
do not), but they return the name of the module, not the module itself.
Here be dragons!
It gets really weird if you mix the two import-types…
>>> from myLib import st
>>> st
'Pre!'
# Modify the value of `st` in myLib.py
>>> from importlib import reload
>>> reload(myLib)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'myLib' is not defined
>>> import myLib
>>> myLib.st # it reused the `myLib` from memory
'Pre!'
>>> st
'Pre!'
>>> reload(myLib)
<module 'myLib' from '/home/user/myLib.py'>
>>> st # still the originally-imported string
'Pre!'
>>> myLib.st
'Post!'
I can’t explain this phenomena, other than to assume that Idle somehow works off of a snapshot
of the filesystem, taken at launch-time.
Repro steps:
- create a
myLib.py
file with (e.g.):
#!/usr/bin/env python3
pre_launch_str = "Pre-launch!"
# post_launch_str = "Post-launch!"
- launch Idle (from the containing folder)
from myLib import pre_launch_str
works as expected: the string is imported/usable- Keep Idle running/open
- [from another application/terminal] modify
myLib.py
to include a new object (e.g.)post_launch_str
from myLib import post_launch_str
will throw an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name 'post_launch_str' from 'myLib' (/home/myUser/myLib.py)```
Anyone know what the cause of this is?
Linux (zsh) + Python 3.10 inthe example above, but I've noticed this long ago (~Python3.5 and on MacOS too)
This has nothing to do with IDLE. When you’ve already put a module into scope (regardless of which names you imported from it), Python will never reload that module. It assumes the module’s code has not changed and will use the in-memory version.
If you’re planning to modify the module live during development, you’ll need to use importlib
to reload it. You need a reference to the module itself. If you imported the module name as import myLib
, then myLib
will do. Otherwise, take a class or function that you imported and get its __module__
(so pre_launch_str.__module__
in your example). Then take that and call reload
on it.
from importlib import reload
reload(pre_launch_str.__module__)
tl;dr: importlib.reload(myLib)
does allow you to grab up-to-date content from myLib.py, but only if you imported the whole module with import myLib
.
Based on Silvio’s answer, I did some playing around:
- when using
import X
, it is possible to usereload(X)
(note the lack of quotes aroundX
) - when using
from X import Y
reload(Y.__module__)
will throw:TypeError: reload() argument must be a module
reload(X)
will throw:NameError: name 'X' is not defined
As Silvio noted, class
and function
objects have the .__module__
attribute (while str
, list
, dict
do not), but they return the name of the module, not the module itself.
Here be dragons!
It gets really weird if you mix the two import-types…
>>> from myLib import st
>>> st
'Pre!'
# Modify the value of `st` in myLib.py
>>> from importlib import reload
>>> reload(myLib)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'myLib' is not defined
>>> import myLib
>>> myLib.st # it reused the `myLib` from memory
'Pre!'
>>> st
'Pre!'
>>> reload(myLib)
<module 'myLib' from '/home/user/myLib.py'>
>>> st # still the originally-imported string
'Pre!'
>>> myLib.st
'Post!'