0

Module A imports a class C from B.

A has a 'run' procedure which inter alia creates an instance of C. After the first run, module B is updated without exiting A; then a second run is done.

Will the new instance of C be from the updated version of B or the original?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RFlack
  • 436
  • 1
  • 5
  • 19
  • Modules are generally only imported once; even if you separately import them in other parts of the program, that's really just a lookup (in `sys.modules`). See e.g. http://stackoverflow.com/q/13128114/3001761, http://stackoverflow.com/q/15280157/3001761. How have you ended up with this structure? – jonrsharpe Oct 06 '15 at 14:54
  • Not sure I'd call it a structure :) Im debugging 'B' (a module with a class for showing a progress bar), and 'A' is a simple test program (with a run and a quit button) - I made a change in B, saved it hit the run button in A and the change wasnt reflected. I had to quit A and restart ... I wanted to check if thats the correct behaviour. I guess Im asking if the reference back to the source of B is on import or on instantiation. – RFlack Oct 06 '15 at 15:02
  • 1
    Ah, I see; if you're debugging and want to see the changes to the module, you'll need to either restart the program or `reload` it. – jonrsharpe Oct 06 '15 at 15:02
  • 1
    Possible duplicate of [Proper way to reload a python module from the console](http://stackoverflow.com/questions/2534480/proper-way-to-reload-a-python-module-from-the-console) – jonrsharpe Oct 06 '15 at 15:03
  • I see a box at top of thie Qu asking if the other Answer (the one you suggest) solves my problem. Im not quite sure whether to say yes or not. Most of it is about reload() which I really dont think I want to use so that doesnt solve my problem or answer my question... but there are a cpl of comments that mention testing libraries eg unittest ... I do think I should check those out which may well help me. My other question is exactly what happens at import (of a class) vs instantiation of the class. – RFlack Oct 06 '15 at 17:03
  • That question is also answered on SO. If you have two questions, ask them separately so they can be closed accurately! – jonrsharpe Oct 06 '15 at 17:05
  • i'll try ... not sure its easy to do succinctly as the other Answer is partly irrelevant to me (all the stuff about reload) but the tips on doctest and unittest, with which I am not familiar, may be helpful (wont know till I look!). And as to what happens when, there is a partial answer here (modules not necessarily recompiled when source changes which I find a bit odd ... have source and object out of sync... I probably have to go read more about the compilation). I'll ponder this and clean it up later today. – RFlack Oct 06 '15 at 17:17

2 Answers2

0

That depends on you notion of update:

Remember that Python is a compiled language: A module is read and compiled into byte code. So when you change the source file, nothing happens because the code was already compiled.

Simply importing a module repeatedly also does nothing, the importer simply checks sys.modules and returns the already existing module from there. A module load is only triggered when you load an unknown module (according to sys.modules).

There is also no auto-check for changed source files, so source files are not automatically recompiled when they have changed.

However, compiled files (.pyc, .pyo) are checked against their source files before they are used. If the corresponding source files are newer or have a different size, recompilation happens for fresh loads (not on import, on load). Note, however, that the .pyc timestamp resolution is only 32 bits, so the actual file system timestamp is truncated.

You can jump through some serious hoops to make Python import a changed source file:

sys.path.insert(0, os.getcwd())

with open("B.py", 'w') as bfile:
    bfile.write("""
class B:
    def act(self):
        print "First version"
    """)

import B
first_class = B.B()

with open("B.py", 'w') as bfile:
    bfile.write("""
class B:
    def act(self):
        print "Second version"

    """)
try:
    os.unlink('B.pyc')
except OSError:
    pass
try:
    os.unlink('B.pyo')
except OSError:
    pass

reload(B)

second_class = B.B()

first_class.act()
second_class.act()

This is actually by coincidence, but the code shows one of the real problems that occur with reimporting and recompilation. Because the two B.pys are created quickly after another, their timestamps more often than not compare equal to the .pyc timestamp. You might get lucky and hit the actual timestamp flip, but that's just luck. Since the files are also the same size on Unix (note the extra newline for the second version), the file size check also reports both source files to be equal.

If you remove the unlink() operations, you --most of the time-- get no module recompile. Instead you get the version version of B loaded from the .pyc file, even though it does not match B.py any more.

In any case, the code objects from the initial import are retained. In this example, first_class is from the initial version of B.py, second_class is from the updated version. The first class is already compiled into byte code and in memory, it does not change just because you change its source file.

For all practical purposes, both classes are from different modules that incidentally happen to have the same source file.

This is probably only useful for debugging and I strongly advise against using it for anything productive. This is especially true, if your module has more than the single source file.

That said, reload() doesn't exist any more in Python 3, because it never worked as expected even in Python 2.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dhke
  • 15,008
  • 2
  • 39
  • 56
  • When is the source compiled? Or re-compiled. – RFlack Oct 06 '15 at 17:04
  • When the module is loaded (or re-loaded) and there isn't already a suitable compiled version (.pyc, .pyo) around. Note that django as a full implementation of a [*reload on change*](https://github.com/django/django/blob/master/django/utils/autoreload.py) for its development server. In practice, however, it still needs to restart the server so that any code changes are actually picked up, as otherwise existing objects would be used in their old version. – dhke Oct 06 '15 at 17:17
  • Thanks. Just to be clear, 'loaded' means imported? And, 'suitable' would not include a check of compile data against the source date? – RFlack Oct 06 '15 at 20:56
  • @RFlack *import* is different from load. If you import a module you pull the name(s) into your namespace. The import *might* trigger a load when the module is not yet loaded. When recompilation happens is described [here](https://stackoverflow.com/questions/15839555/when-are-pyc-files-refreshed#answer-15839646). The timestamp resolution for `.pyc` is "only" 32bits, however, hence the explicit unlinks in the above code. – dhke Oct 07 '15 at 07:30
0

No, it will probably use the original. When you import the class C from B in A you will create a reference to an object in A (happens to be a class) called C.

If you don't reassign to C it will still refer to the same object, so unless you actually modify the very same object during the update of B the changes wouldn't be visibly from A via the use of C.

Now for some examples of how you might have done:

If you just edit the source of B (after the input in the below code) and have the following python code:

from B import C

c1 = C()

input("Modify B!")

c2 = C()

Then you haven't modified anything as far as A is concerned, it even didn't bother to look at your modified source.

Now let's try to reimport:

from B import C

c1 = C()

input("Modify B!")

from B import C

c2 = C()

Now you do reassign to C, but still Python does not bother to look at your modification. The second import first checks if it has loaded the B module, and it has and then it just grabs C from it and puts into As namespace.

Now let's try a little bit harder:

import B
from B import C

c1 = C()

input("Modify B!)

reload(B)

c2 = C()

Then still no luck; the reload(B) only tells the interpreter to reload the module, so now B refers to the new module, but C wasn't updated in this process.

Now for the nearest you get:

import B
from B import C

c1 = C()

input("Modify B!")

reload(B)
from B import C

c2 = C()

Now c2 = C() will use the class definition from the modified B, but be aware that the c1 did use the old definition and its type is still the old version of the class C.

Last, as I mentioned, actually modifying the very same object I guess I'd give an example:

class C:
    pass

c = C()

def fubar(self):
    return 42

C.fubar = fubar

c.fubar()

Here the class C is first defined and an object is created. Then I modify the class C by adding a method to it. The type of c hasn't changed. It's the very same class, but now that class have got a new method since c was created.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
skyking
  • 13,817
  • 1
  • 35
  • 57
  • There's also the nice gimmick that `.pyc` timestamps are 32bit, but up-to-date checking includes the size of the original source. Rewrite a source file with the same size quickly and the change doesn't get picked up. – dhke Oct 07 '15 at 13:38