Reputation: 4963
Assume I have two modules:
first.package.module
# Assign module to a variable
which_module = ???
print("I originally live in {}".format(__name__)) # Prints first.package.module
print("I was run from {}".format(which_module))
second.package.module
from first.package.module import *
third.package.module
from first.package.module import *
How can I get the second row, "I was run from", to print second.package.module after the import in second.package.module? Or third.package.module when it is run from there.
The reason I want this behaviour is due to building an app in Django that is used several times in the same project, i.e. there are several instances of the same app. Each of these instances has their own models, which inherits from an abstract model. To build reusable views and urls, I want to load the models dynamically, and to do that, I need to know which module that has imported the app and runs it.
Upvotes: 1
Views: 131
Reputation: 1124000
You can print the name of the module triggering the module load with:
import sys
print sys._getframe(1).f_globals['__name__']
In Python 3 with the new importlib
stack you'll need to increase the framecount to 6 (at least for 3.5, 3.6 and 3.7):
print(sys._getframe(6).f_globals['__name__'])
A cross-CPython solution would be to filter out any importlib._bootstrap*
elements in the stack:
import sys
def imported_from(depth=0):
# skip the frames of this function, and the caller
f = sys._getframe(2 + depth)
while f and f.f_code.co_filename.startswith("<frozen importlib._bootstrap"):
f = f.f_back
return f and f.f_globals['__name__']
print(imported_from())
The above imported_from()
function can be defined in a utility module and imported; the sys._getframe(2)
call ensures that the starting context is whatever triggered the imported_from()
call. Provide a positive integer as the depth
argument to increase the number of frames skipped. The function works on any Python version that provides a sys._getframe()
function, whether or not it uses the importlib
stack.
Note that this:
Relies on a CPython implementation detail (exposing the frame stack is not available on other Python implementations). See the sys._getframe()
function documentation:
CPython implementation detail: This function should be used for internal and specialized purposes only. It is not guaranteed to exist in all implementations of Python.
In Python 3, it depends on the exact implementation details of the importlib
stack; ongoing development could add or remove calls in that stack. For example, in Python 3.3, which first introduced importlib
, the correct stack count to skip is 9, in 3.4 it went down to 7, and 3.5 dropped it to 6 (a number that's since been stable). imported_from()
works around this, but introduces a new implementation detail-dependent assumption: that the stack frames involved with importing can be detected by looking for the <frozen importlib._bootstrap
prefix in the filename. In theory it is possible to compile CPython with importlib._bootstrap
left un-frozen (not included in the interpreter binary as array of marshal
data).
Only works when a module is imported for the first time, at which point Python executes the module code to produce a sys.modules
object.
I cannot emphasise the last point enough. Python only ever loads a module once. Importing is a two-step process: loading and binding. The load step is only executed if the module object doesn't exist yet, all other imports for the module only bind names.
Binding names is not something you can practically hook into; import modulename
is nothing more than a modulename = sys.modules['modulename']
assignment once the module is already in memory.
Upvotes: 3