Python: determine actual current module (not __main__)

5.7k views Asked by At

I'm trying to determine the actual current module of a function (as seen if imported from elsewhere), even if the current module is the "top level scripting environment" __main__.

It may sound like a weird thing to do, but the background is that I need to serialize a function and unserialize it (including arguments) on a different machine, for which I need to make sure the correct module AND NOT __main__ is imported before deserializing (otherwise I get an error saying AttributeError: 'module' object has no attribute my_fun).

So far, I've tried inspection:

import inspect
print inspect.getmodule(my_fun)

which gives me

<module '__main__' from 'example.py'>

of course. I also tried finding something useful using globals(), no luck.

What I really want is <module 'example' from 'example.py'>. I suppose a hacky way would be to parse it from the file name using something like

m_name = __main__.__file__.split("/")[-1].replace(".pyc","")

and then find the module by name sys.modules[m_name].

Is there a cleaner/better way to do this?

EDIT: After learning about ipython's "FakeModule" and a bit more googling, I came accross this post, which describes exactly the problem that I'm facing, including my current solution to it (which is explicitly importing the current module import current_module and serializing current_module.my_fun instead of my_fun). I'm trying to avoid this, as it might not be intuitive for the users of my package.

6

There are 6 answers

5
Winston Ewert On BEST ANSWER

I actually ran across this same problem.

What I used was:

return os.path.splitext(os.path.basename(__main__.__file__))[0]

Which is effectively the same as your "hack." Honestly, I think its the best solution.

0
DMH On

One way you can do this -- possibly not the best way, but it works for me -- is to import your modules with __import__ and use getattr in a way something like the following.

(Here I am using some ideas described in this post about dynamically loading modules.)

def dynamic_import(name):
    mod = __import__(name)
    components = name.split('.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod

tmodule = dynamic_import('modA')
# Print the module name
print tmodule
# Use the module's contents
t = tmodule.myObject()
t.someMethod()

Where modA.py looks like this:

class myObject():
    def someMethod(self):
        print "I am module A"

So you can see we're getting the name of the module we've imported and still getting to use the objects and methods inside the module in a normal way. When I run this I get the following:

python experiment.py 
<module 'modA' from 'modA.pyc'>
I am module A

Again, this may or may not be the "ideal" way, but it works well and as far as I can tell doesn't entail any undesirable tradeoffs in most cases. Hope this helps.

4
sneakers-the-rat On

I don't think any of the existing answers actually answer the question directly: how do you get the name of a module when it is run as __main__ ?

using inspect for most of the steps...

import inspect

def module_name(obj):
    module_name = obj.__module__

    if "__main__" in module_name:
        # get parent modules of object
        mod_obj = inspect.getmodule(obj) # type: module

        # from the filename of the module, get its name
        mod_suffix  = inspect.getmodulename(inspect.getmodule(obj).__file__)

        # join parent to child with a .
        module_name = '.'.join([mod_obj.__package__, mod_suffix])

    return module_name

edit: as bluenote10 points out below, if you aren't doing anything whacky with your import paths and module names you can just do inspect.getmodule(obj).__spec__.name

0
robotsc02 On

As of python 3.4,

importlib.util.find_spec('__main__').name
0
alexis On

Edit: In retrospect, by far the best and cleanest solution is to avoid being in this situation in the first place; if it's your code that is being serialized, move all serializable functions to modules that are loaded by the main program script. This makes the origin of the function retrievable under any and all circumstances, without any need for hacks or special cases.

If that's not possible, I think your original solution (to retrieve the module name from __main__.__file__) is best and simplest. If you are worried about it seeming counter-intuitive for your users, wrap it in a nice function and document what it's for.

When you run a module as __main__, python really doesn't associate it with its normal module name: If you import example, it will load the file a second time as if it's a separate module. In fact this probably happens in your case, otherwise you wouldn't be able to find your module by name in sys.modules: Module example and module __main__ really are separate runtime objects, as you'll find out if you explicitly change a module variable in one of them.

3
boses On

I know this is outdated but I found a simpler solution in Python3 that worked for me. Long story short, there's the object's _spec_ which also stores the actual module name instead of being "_main_".

import inspect
if obj.__class__.__module__ == "__main__":
    print(inspect.getmodule(obj).__spec__.name)