Reputation: 35720
Imagine a Python script that will take a long time to run, what will happen if I modify it while it's running? Will the result be different?
Upvotes: 380
Views: 102581
Reputation: 2836
Older question, but I feel like there's an interesting answer just because you didn't specify how you were running the code. If you import my_module
, it's pretty simple: nothing changes unless you're using importlib or some other library that specifically enables replacing the module in memory.
But there are other ways to execute code within the context of a long running process. For example, you may have a timer / supervisor process that periodically executes routines. If you import
the routine modules, they'll stay in memory as long as the supervisor runs. But if you spawn a new process that imports and runs the module, you get the latest version of whatever is in the file (for better or for worse). You also end up creating another process, which may or may not be what you want. But it's fun to try things.
The simplest, most 'redneck engineering' way I can think of to demonstrate this is just using os.system()
. Here's a file timer.py:
#timer.py
import schedule
import time
import os
import pottery
# Executes super important pottery routine every minute.
# But we want some consistent pottery and also some
# more experimental pottery!
def make_consistent():
pottery.make()
def make_wabisabi():
os.system("python -c 'import pottery; pottery.make()'")
schedule.every(1).minutes.do(make_consistent)
schedule.every(1).minutes.do(make_wabisabi)
while True:
schedule.run_pending()
time.sleep(1)
And here is the file it runs both as an imported module, and using os.system():
# pottery.py
def make():
print("Here's a pot for you!")
Execute it:
$ python timer.py
Here's a pot for you!
Here's a pot for you!
After the first run change the string in pottery.py while timer.py is still running:
Here's a pot for you!
Oh my! Well, here's a lumpy pot!
As you see, the module imported by timer.py didn't change, but if we use any method that doesn't keep the module loaded into memory, we get the latest version of the code. While this didn't do any dynamic reloading or fancy things, you can see that the result is different.
Upvotes: 0
Reputation: 355
Nothing, as this answer. Besides, I did experiment when multiprocessing
is involved. Save the script below as x.py
:
import multiprocessing
import time
def f(x):
print(x)
time.sleep(10)
if __name__ == '__main__':
with multiprocessing.Pool(2) as pool:
for _ in pool.imap(f, ['hello'] * 5):
pass
After python3 x.py
and after the first two 'hello' being printed out, I modified ['hello']
to ['world']
and observed what happend. Nothing interesting happened. The result was still:
hello
hello
hello
hello
hello
Upvotes: 1
Reputation: 178
If you run the following script:
from time import sleep
print("Printing hello world in: ")
for i in range(10, 0, -1):
print(f"{i}...")
sleep(1)
print("Hello World!")
Then change "Hello World!" to "Hello StackOverflow!" while it's counting down, it will still output "Hello World".
Upvotes: 3
Reputation: 1268
This is slightly different from what you describe in your question, but it works:
my_string = "Hello World!"
line = input(">>> ")
exec(line)
print(my_string)
Test run:
>>> print("Hey")
Hey
Hello World!
>>> my_string = "Goodbye, World"
Goodbye, World
See, you can change the behavior of your "loaded" code dynamically.
Upvotes: -3
Reputation: 847
No, the result will not reflect the changes once saved. The result will not change when running regular python files. You will have to save your changes and re-run your program.
Upvotes: 2
Reputation: 1145
This is a fun question. The answer is that "it depends".
Consider the following code:
"Example script showing bad things you can do with python."
import os
print('this is a good script')
with open(__file__, 'w') as fdesc:
fdesc.write('print("this is a bad script")')
import bad
Try saving the above as "/tmp/bad.py" then do "cd /tmp" and finally "python3 bad.py" and see what happens.
On my ubuntu 20 system I see the output:
this is a good script
this is a bad script
So again, the answer to your question is "it depends". If you don't do anything funky then the script is in memory and you are fine. But python is a pretty dynamic language so there are a variety of ways to modify your "script" and have it affect the output.
If you aren't trying to do anything funky, then probably one of the things to watch out for are imports inside functions.
Below is another example which illustrates the idea (save as "/tmp/modify.py" and do "cd /tmp" and then "python3 modify.py" to run). The fiddle
function defined below simulates you modifying the script while it is running (if desired, you could remove the fiddle function, put in a time.sleep(300)
at the second to last line, and modify the file yourself).
The point is that since the show
function is doing an import inside the function instead of at the top of the module, the import won't happen until the function is called. If you have modified the script before you call show
, then your modified version of the script will be used.
If you are seeing surprising or unexpected behavior from modifying a running script, I would suggest looking for import statements inside functions. There are sometimes good reasons to do that sort of thing so you will see it in people's code as well as some libraries from time to time.
Below is the demonstration of how an import inside a function can cause strange effects. You can try this as is vs commenting out the call to the fiddle
function to see the effect of modifying a script while it is running.
"Example showing import in a function"
import time
def yell(msg):
"Yell a msg"
return f'#{msg}#'
def show(msg):
"Print a message nicely"
import modify
print(modify.yell(msg))
def fiddle():
orig = open(__file__).read()
with open(__file__, 'w') as fdesc:
modified = orig.replace('{' + 'msg' + '}', '{msg.upper()}')
fdesc.write(modified)
fiddle()
show('What do you think?')
Upvotes: 20
Reputation: 4936
When you run a python program and the interpreter is started up, the first thing that happens is the following:
sys
and builtins
is initialized__main__
module is initialized, which is the file you gave as an argument to the interpreter; this causes your code to executeWhen a module is initialized, it's code is run, defining classes, variables, and functions in the process. The first step of your module (i.e. main file) will probably be to import other modules, which will again be initialized in just the same way; their resulting namespaces are then made available for your module to use. The result of an importing process is in part a module (python-) object in memory. This object does have fields that point to the .py and .pyc content, but these are not evaluated anymore: module objects are cached and their source never run twice. Hence, modifying the module afterwards on disk has no effect on the execution. It can have an effect when the source is read for introspective purposes, such as when exceptions are thrown, or via the module inspect.
This is why the check if __name__ == "__main__"
is necessary when adding code that is not intended to run when the module is imported. Running the file as main is equivalent to that file being imported, with the exception of __name__
having a different value.
Sources:
__main__
module: __main__
- Top-level code environmentUpvotes: 30
Reputation: 47
depending. if a python script links to other modified file, then will load newer version ofcourse. but if source doesnt point to any other file it'll just run all script from cache as long as its run. changes will be visible next time...
and if about auto-applying changes when they're made - yes, @pcbacterio was correct. its possible to do thar but script which does it just remembers last action/thing what was doing and checks when the file is modified to rerun it (so its almost invisible)
=]
Upvotes: -5
Reputation: 1152
It happens nothing. Once the script is loaded in memory and running it will keep like this.
An "auto-reloading" feature can be implemented anyway in your code, like Flask and other frameworks does.
Upvotes: -2
Reputation: 7308
Nothing, because Python precompiles your script into a PYC file and launches that.
However, if some kind of exception occurs, you may get a slightly misleading explanation, because line X may have different code than before you started the script.
Upvotes: 451