Can Python recognize changes to a file that it is running interactively?

2024/9/22 20:23:28

I was doing some troubleshooting and I was curious if it is possible to run a Python script interactively, change a function defined in the script, save the file, then have the interactive shell recognize the changes. Here is an example of what I am doing currently:

my_script.py:

def dummy_func():print('Something')
def main():dummy_func()
if __name__ == '__main__':main()

I go to my terminal and run:

>python -i my_script.py
Something
>>>

If I go back to my_script.py in my editor and make the following change:

def dummy_func():print('Something else')

Then go back to the terminal (which is still open) and re-run the updated function:

>>>dummy_func()
Something
>>>

Is it possible to do something to instead get the following behavior?:

>>>dummy_func()
Something else
>>>

I know it is possible to reload modules using importlib and reload but as far as I can tell that does not apply here since I am not importing anything.

I think this may be distinct from How do I unload (reload) a Python module?. I am asking if there is a way to reload the current file you are running interactively through the python shell, while that question is asking about reloading a module you have imported into another python script.

Answer

From what I can find, the short answer is:
No, normally the Python interpreter does not recognize changes to a file once that file has been parsed, analyzed, and fed into the interpreter.

What you should do instead apparently is use your .py file as a module, import that as a module into another .py file, then run that new file. This allows your first file to be reloaded through the interactive interpreter. Here's an example:

from importlib import reload  # Python 3.4+ only.
import foowhile True:# Do some things.if is_changed(foo):foo = reload(foo)

I am still a little fuzzy on the details, but maybe someone can help fill those in. As far as I can tell from the sources I linked below, the interpreter basically takes some steps to load your program from the saved python file into memory (glossing over a lot of details). Once this process has been performed, the interpreter does not perform it again unless you explicitly ask it to do so, for example by using the importlib's reload() function to again perform the process.

Sources:

How do I unload (reload) a Python module? (quoted above)

A Python Interpreter Written in Python:
This link has a lot more information about how the interpreter works, and I found this section particularly helpful:

Real Python Bytecode
At this point, we'll abandon our toy instructionsets and switch to real Python bytecode. The structure of bytecode issimilar to our toy interpreter's verbose instruction sets, except thatit uses one byte instead of a long name to identify each instruction.To understand this structure, we'll walk through the bytecode of ashort function. Consider the example below:

>>> def cond():  
...     x = 3  
...     if x < 5:  
...         return 'yes'  
...     else:  
...         return 'no'  
...  

Python exposes a boatload of its internals at run time, and we can access them rightfrom the REPL. For the function object cond, cond.code is the codeobject associated it, and cond.code.co_code is the bytecode.There's almost never a good reason to use these attributes directlywhen you're writing Python code, but they do allow us to get up to allsorts of mischief—and to look at the internals in order to understandthem.

>>> cond.__code__.co_code  # the bytecode as raw bytes  b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x00\x00S'
>>> list(cond.__code__.co_code)  # the bytecode as numbers  
[100, 1, 0, 125, 0, 0, 124, 0, 0, 100, 2, 0, 107, 0, 0, 114, 22, 0, 100, 3, 0, 83,
100, 4, 0, 83, 100, 0, 0, 83]  

When we just print the bytecode, itlooks unintelligible—all we can tell is that it's a series of bytes.Luckily, there's a powerful tool we can use to understand it: the dismodule in the Python standard library.

dis is a bytecode disassembler. A disassembler takes low-level codethat is written for machines, like assembly code or bytecode, andprints it in a human-readable way. When we run dis.dis, it outputs anexplanation of the bytecode it has passed.

>>> dis.dis(cond)   2           0 LOAD_CONST               1 (3)3 STORE_FAST               0 (x)3           6 LOAD_FAST                0 (x)9 LOAD_CONST               2 (5)12 COMPARE_OP               0 (<)15 POP_JUMP_IF_FALSE       224          18 LOAD_CONST               3 ('yes')21 RETURN_VALUE6     >>   22 LOAD_CONST               4 ('no')25 RETURN_VALUE26 LOAD_CONST               0 (None)29 RETURN_VALUE  

What does all this mean? Let's look at the first instruction LOAD_CONST as an example. The number in thefirst column (2) shows the line number in our Python source code. Thesecond column is an index into the bytecode, telling us that theLOAD_CONST instruction appears at position zero. The third column isthe instruction itself, mapped to its human-readable name. The fourthcolumn, when present, is the argument to that instruction. The fifthcolumn, when present, is a hint about what the argument means.

How does the Python Runtime actually work?:

With Python, it uses an interpreter rather than a compiler. Aninterpreter works in exactly the same way as a compiler, with onedifference: instead of code generation, it loads the output in-memoryand executes it directly on your system. (The exact details of howthis happens can vary wildly between different languages and differentinterpreters.)

importlib — The implementation of import:

When reload() is executed:

Python module’s code is recompiled and the module-level codere-executed, defining a new set of objects which are bound to names inthe module’s dictionary by reusing the loader which originally loadedthe module. The init function of extension modules is not called asecond time.

Again, please let me know if I need to edit this answer to follow etiquette.

https://en.xdnf.cn/q/71905.html

Related Q&A

How to use FTP with Pythons requests

Is it possible to use the requests module to interact with a FTP site? requests is very convenient for getting HTTP pages, but I seem to get a Schema error when I attempt to use FTP sites. Is there …

How to find a best fit distribution function for a list of data?

I am aware of many probabilistic functions builted-in Python, with the random module. Id like to know if, given a list of floats, it would be possible to find the distribution equation that best fits t…

How to use peewee limit()?

With Peewee Im trying to use limit as follows:one_ticket = Ticket.select().limit(1) print one_ticket.count()This prints out 5 however. Does anybody know whats wrong here?

Python (1..n) syntax?

I see in the code on this Sage wiki page the following code:@interact def _(order=(1..12)):Is this (1..n) syntax unique to Sage or is it something in Python? Also, what does it do?

in python , How to load just one time a ML model in a rest service like django or flask?

I have a ML model saved in a pkl (pickel file), I have no problem loading this model and using it for prediction, even I have a rest service that expose it, the only problem is that I load the model i…

Adding a Title or Text to a Folium Map

Im wondering if theres a way to add a title or text on a folium map in python?I have 8 maps to show and I want the user to know which map theyre looking at without having to click on a marker. I attem…

Zombie SharedDataMiddleware on Python Heroku

Im setting up a Flask app on Heroku. Everything is working fine until I added static files. Im using this:from werkzeug import SharedDataMiddleware app = Flask(__name__) app.wsgi_app = SharedDataMiddle…

Python: Urllib.urlopen nonnumeric port

for the following codetheurl = "https://%s:%[email protected]/nic/update?hostname=%s&myip=%s&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG" % (username, password, hostname, theip)conn…

How to unittest the sequence of function calls made inside a python fuction?

I would like to unittest a fuction and assert if the sequence of function calls made inside the function workflow(). Something like,[1st called] fetch_yeargroup_ls()[2nd called] invoke_get_links().....…

OpenCV: Extract SURF Features from user-defined keypoints

I want to compute SURF Features from keypoints that I specify. I am using the Python wrapper of OpenCV. The following is the code I am trying to use, but I cannot find a working example anywhere.surf…