It might be late to answer, but I didn't found the correct answer for myself.
The most closest and precise solution (faster than inspect.stack()
) in the python 3.7.x
:
# search for first module in the stack
stack_frame = inspect.currentframe()
while stack_frame:
print('***', stack_frame.f_code.co_name, stack_frame.f_code.co_filename, stack_frame.f_lineno)
if stack_frame.f_code.co_name == '<module>':
if stack_frame.f_code.co_filename != '<stdin>':
caller_module = inspect.getmodule(stack_frame)
else:
# piped or interactive import
caller_module = sys.modules['__main__']
if not caller_module is None:
#... do something here ...
break
stack_frame = stack_frame.f_back
Pros:
- Preciser than
globals()
method.
- Does not depend on the stack intermediate frames, which can be added for example, via hooking or by the 3dparty tools like
pytest
:
*** foo ... ..
*** boo ... ..
*** runtest c:\python\x86\37\lib\site-packages\xonsh\pytest_plugin.py 58
*** pytest_runtest_call c:\python\x86\37\lib\site-packages\_pytest\runner.py 125
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** <lambda> c:\python\x86\37\lib\site-packages\_pytest\runner.py 201
*** from_call c:\python\x86\37\lib\site-packages\_pytest\runner.py 229
*** call_runtest_hook c:\python\x86\37\lib\site-packages\_pytest\runner.py 201
*** call_and_report c:\python\x86\37\lib\site-packages\_pytest\runner.py 176
*** runtestprotocol c:\python\x86\37\lib\site-packages\_pytest\runner.py 95
*** pytest_runtest_protocol c:\python\x86\37\lib\site-packages\_pytest\runner.py 80
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** pytest_runtestloop c:\python\x86\37\lib\site-packages\_pytest\main.py 258
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** _main c:\python\x86\37\lib\site-packages\_pytest\main.py 237
*** wrap_session c:\python\x86\37\lib\site-packages\_pytest\main.py 193
*** pytest_cmdline_main c:\python\x86\37\lib\site-packages\_pytest\main.py 230
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** main c:\python\x86\37\lib\site-packages\_pytest\config\__init__.py 90
*** <module> c:\Python\x86\37\Scripts\pytest.exe\__main__.py 7
- Can handle python piped or interactive session.
Cons:
- A kind of much precise and can return modules registered in an executable like for the
pytest.exe
which might not what you want.
inspect.getmodule
still may return None on valid modules depending on hooking
I have an extension to the python:
How to import a module given the full path?
The extension having wrapper functions for that case:
def tkl_get_stack_frame_module_by_offset(skip_stack_frames = 0, use_last_frame_on_out_of_stack = False):
...
def tkl_get_stack_frame_module_by_name(name = '<module>'):
...
You have to just initialize the extension properly:
# portable import to the global space
sys.path.append(<path-to-tacklelib-module-directory>)
import tacklelib as tkl
tkl.tkl_init(tkl, global_config = {'log_import_module':os.environ.get('TACKLELIB_LOG_IMPORT_MODULE')})
# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()
# use `tkl_*` functions directly from here ...