Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get module instance given its vars dict

Tags:

python

Suppose I have the dict of a module (via vars(mod), or mod.__dict__, or globals()), e.g.:

import mod

d = vars(mod)

Given the dict d, how can I get back the module mod? I.e. I want to write a function get_mod_from_dict(d), which returns the module if the dict belongs to a module, or None:

>>> get_mod_from_dict(d)
<module 'mod'>

If get_mod_from_dict returns a module, I must have that this holds:

mod = get_mod_from_dict(d)
assert mod is None or mod.__dict__ is d

I actually can implement it like this:

def get_mod_from_dict(d):
    mods = {id(mod.__dict__): mod for (modname, mod) in sys.modules.items()
                                  if mod and modname != "__main__"}
    return mods.get(id(d), None)

However, this seems inefficient to me, to iterate through sys.modules.

Is there a better way?


Why do I need this?

  • In some cases, you get access to the dict only. E.g. in the stack frames. And then, depending on what you want to do, maybe just for inspection/debugging purpose, it is helpful to get back the module.

  • I wrote some extension to Pickler which can pickle methods, functions, etc. Some of these have references to the module, or the module dict. Wherever I have a dict which belongs to a module during pickling, I don't want to pickle the dict, but instead a reference to the module.

like image 404
Albert Avatar asked May 16 '19 15:05

Albert


2 Answers

Every module has a __name__ attribute that uniquely identifies the module in the import system:

>>> import os
>>> os.__name__
'os'
>>> vars(os)['__name__']
'os'

Imported modules are also cached in sys.modules, which is a dict mapping module names to module instances. You can simply look up the module's name there:

import sys

def get_mod_from_dict(module_dict):
    module_name = module_dict['__name__']
    return sys.modules.get(module_name)

Some people have expressed concern that this might not work for (sub-)modules in packages, but it does:

>>> import urllib.request
>>> get_mod_from_dict(vars(urllib.request))
<module 'urllib.request' from '/usr/lib/python3.7/urllib/request.py'>

There is a very minor caveat, though: This will only work for modules that have been properly imported and cached by the import machinery. If a module has been imported with tricks like How to import a module given the full path?, it might not be cached in sys.modules and your function might then unexpectedly return None.

like image 152
Aran-Fey Avatar answered Oct 12 '22 15:10

Aran-Fey


You can use importlib.import_module to import a module given it's name. Example for numpy


In [77]: import numpy 
    ...: import importlib                                                                                                                                                                               

In [78]: d = vars(numpy)                                                                                                                                                                                

In [79]: np = importlib.import_module(d['__name__'])                                                                                                                                                    

In [80]: np.array([1,2,3])                                                                                                                                                                              
Out[80]: array([1, 2, 3])
like image 39
Devesh Kumar Singh Avatar answered Oct 12 '22 15:10

Devesh Kumar Singh