user3064538
user3064538

Reputation:

Is it possible to have two modules with the same __name__?

I'm trying to get all the functions that are defined in a given module (as opposed to imported by it), which I do like this:

from inspect import getmembers, isfunction
import my_module

fns = [fn for fn in vars(my_module).values() if isfunction(fn) and fn.__module__ == my_module.__name__]

The problem with the above is that if it's possible for my_module to import a function from other_module where other_module.__name__ == my_module.__name__, then the above code will erroneously mark that function as defined in my_module when it was actually defined in other_module and imported by my_module. I've been trying to create such a scenario but I can't seem to do that. I also know there's a sys.modules which is a dictionary mapping strings to modules, which makes me think that maybe module names must be unique in the current scope. Is this possible?

Upvotes: 0

Views: 394

Answers (1)

Henry Tjhia
Henry Tjhia

Reputation: 752

TL;DR of my finding is that module files may have a shared name, but the actually saved in the Python's sys.modules is unique. Hence, it can be concluded that it is not possible for two or more modules to have the same name in Python.

To illustrate, we setup the following structures and explanation follows:

#add C:\superflous to the PYTHONPATH
C:\
    superfluous\
        mod.py
    
D:\
    root\
        mod.py

The question states:

if it's possible for my_module to import a function from other_module where other_module.__name__ == my_module.__name__

For two modules to have the same __name__, the module files name must be the same. But how to do it in Python context? We know we can't have two files with same name in a directory.

And, if we put in the ...sub\mod.py the following line:

#...sub\mod.py
from mod import *

#or

import mod

...sub\mod.py will just import itself and sys.modules will include mod as one of his dict keys. We would like to import a module having the same as mod, so let's import mod from the C:superfluous\mod.py. To do this we first put an __init__.py inside sub to turn it into a package directory and also to enable absolute import. In addition, let's add main.py to the sub directory. Our final structure:

D:\
    root\
        __init__.py
        main.py
        mod.py

We define main.py and both of the mod.py as follows:

#root\main.py

from . import mod
import sys
import types

print(f"{'mod:':<12}{{}}".format(sys.modules['mod']))
print(f"{'root.mod:':<12}{{}}".format(sys.modules['root.mod']))
print(f"{'-'*60}")

function_from_root_mod = (root_mod := sys.modules['root.mod'].__dict__).keys() - sys.modules['__main__'].__dict__

for i in function_from_root_mod:
    if isinstance((root_mod_values := root_mod[i]), types.FunctionType) and root_mod_values.__module__ == 'root.mod':
        print(f'func_name => {i} from modules "{root_mod_values.__module__}"')
#root\mod.py

from mod import *

def sub_mod_func(): pass

var_1 = 1
#C:\superfluous\mod.py

from collections import defaultdict
from itertools import permutations

def c_mod_func(): pass

We then execute main.py through command prompt:

PS D:\> python -m root.main

the following is the output:

mod:        <module 'mod' from 'C:\\superfluous\\mod.py'>
root.mod:   <module 'root.mod' from 'D:\\root\\mod.py'>
------------------------------------------------------------
func_name => sub_mod_func from modules "root.mod"

Important findings:

  • Relative import (.) in main.py translated into root.mod and becomes a key as in sys.modules['root.mod']
  • mod.py in C:\superfluous becomes sys.modules['mod']

Module files may have the same name, but the paths (directory that containing them, e.g., D:\root\mod.py) provide uniqueness for the sys.modules keys.

Upvotes: 1

Related Questions