mngeek206
mngeek206

Reputation: 5177

Can you use Python relative imports to import the __init__ file of the current module under a specific name?

Imagine I have a module with two files, like this:

mymodule
 |-- __init__.py
 `-- submodule.py

mymodule/__init__.py contains:

SOME_CONSTANT_ONE = 1
SOME_CONSTANT_TWO = 2
SOME_CONSTANT_THREE = 3
...
SOME_CONSTANT_ONE_HUNDRED = 100

def initialize():
    pass # do some stuff

def support_function():
    pass # something that lots of other functions might need

I already know that I can use a relative import to bring in specific objects from the __init__.py file, like this:

submodule.py:

from . import initialize, support_function

def do_work():
    initialize() # initialize the module
    print(support_function()) # do something with the support function

But now what I want to know is if I can import all of the constants from the __init__.py file, but simultaneously have them appear in a namespace.

What won't work (what I've tried/considered):

What I really want is something like this:

submodule.py:

SOME_CONSTANT_ONE = "one!" # We don't want to clobber this.

import . as outer_module # this does not work, but it illustrates what is desired.

def do_work():
    print(SOME_CONSTANT_ONE)              # should print "one!"
    print(outer_module.SOME_CONSTANT_ONE) # should print "1"

I know that I could move all of the constants to a constants.py file and then I should be able to import it with from . import constants (as something) but I'm working on existing code and making that change would require a lot of refactoring. While that's not a bad idea, I'm wondering, given that Python does have a way to import individual objects, and also to import the whole module by name to an explicit name, if I can maybe do something with importlib to accomplish importing everything from __init__.py into a namespace?

Upvotes: 2

Views: 234

Answers (2)

wim
wim

Reputation: 363476

The loader sets __package__ which you can use:

import sys

SOME_CONSTANT_ONE = "one!" # We don't want to clobber this.

outer_module = sys.modules[__package__]

def do_work():
    print(SOME_CONSTANT_ONE)              # should print "one!"
    print(outer_module.SOME_CONSTANT_ONE) # should print "1"

This is precisely the attribute from which relative imports are based. See PEP 366 for details.

However, I really think the backwards-compatible refactoring which the other answer suggests is probably the better approach here.

Upvotes: 4

sytech
sytech

Reputation: 41159

I could move all of the constants to a constants.py file and then I should be able to import it with from . import constants (as something) but I'm working on existing code and making that change would require a lot of refactoring

You can still refactor the constants into the new constants.py module. To support existing code relying on __init__.py you can import constants into the __init__.py

# constants.py
SOME_CONSTANT_ONE = 1
SOME_CONSTANT_TWO = 2
SOME_CONSTANT_THREE = 3
... # etc
# __init__.py
from .constants import *
# submodule.py
SOME_CONSTANT_ONE = 'dont clobber me!'
from . import constants as something
print(something.SOME_CONSTANT_ONE) # Yay namespaces
# existing_code.py
from . import SOME_CONSTANT_ONE
# still works!
# no refactor required!

Typically speaking, the __init__.py file is usually left completely empty and nothing is ever directly defined there. If there are contents in __init__.py they are typically imported from within the package. https://stackoverflow.com/a/4116384/5747944

Upvotes: 2

Related Questions