In a big application I am working, several people import same modules differently e.g. import x or from y import x the side effects of that is x is imported twice and may introduce very subtle bugs, if someone is relying on global attributes
e.g. suppose I have a package mypakcage with three file mymodule.py, main.py and init.py
mymodule.py contents
l = []
class A(object): pass
main.py contents
def add(x):
from mypackage import mymodule
mymodule.l.append(x)
print "updated list",mymodule.l
def get():
import mymodule
return mymodule.l
add(1)
print "lets check",get()
add(1)
print "lets check again",get()
it prints
updated list [1]
lets check []
updated list [1, 1]
lets check again []
because now there are two lists in two different modules, similarly class A is different To me it looks serious enough because classes itself will be treated differently e.g. below code prints False
def create():
from mypackage import mymodule
return mymodule.A()
def check(a):
import mymodule
return isinstance(a, mymodule.A)
print check(create())
Question:
Is there any way to avoid this? except enforcing that module should be imported one way onyl. Can't this be handled by python import mechanism, I have seen several bugs related to this in django code and elsewhere too.
Each module namespace is imported only once. Issue is, you're importing them differently. On the first you're importing from the global package, and on the second you're doing a local, non-packaged
import
. Python sees modules as different. The first import is internally cached asmypackage.mymodule
and the second one asmymodule
only.A way to solve this is to always use absolute imports. That is, always give your module absolute import paths from the top-level package onwards:
Remember that your entry point (the file you run,
main.py
) also should be outside the package. When you want the entry point code to be inside the package, usually you use a run a small script instead. Example:runme.py
, outside the package:And in
main.py
you add:I find this document by Jp Calderone to be a great tip on how to (not) structure your python project. Following it you won't have issues. Pay attention to the
bin
folder - it is outside the package. I'll reproduce the entire text here:I can only replicate this if main.py is the file you are actually running. In that case you will get the current directory of main.py on the sys path. But you apparently also have a system path set so that mypackage can be imported.
Python will in that situation not realize that mymodule and mypackage.mymodule is the same module, and you get this effect. This change illustrates this:
But add another mainfile, in the currect directory:
and the result is different:
So I suspect that you have your main python file within the package. And in that case the solution is to not do that. :-)