19

I have a python package named foo, which i use in imports:

import foo.conf
from foo.core import Something

Now i need to rename the foo module into something else, let's say bar, so i want to do:

import bar.conf
from bar.core import Something

but i want to maintain backward compatibility with existing code, so the old (foo.) imports should work as well and do the same as the bar. imports.

How can this be accomplished in python 2.7?

Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162

3 Answers3

14

Do you mean something like this?

import foo as bar

you can use shortcuts for module imports like:

from numpy import array as arr

in: arr([1,2,3])
out: array([1, 2, 3])

and you can also use more than one alias at a time

from numpy import array as foo
in: foo([1,2,3])
out: array([1, 2, 3])

if your foo is a class you can do:

bar=foo()

and call a subfunction of it by:

bar.conf()

Does this help you?

www.pieronigro.de
  • 840
  • 2
  • 12
  • 30
14

This forces you to keep a foo directory, but I think it the best way to get this to work.

Directory setup:

bar
├── __init__.py
└── baz.py
foo
└── __init__.py

foo_bar.py

bar/__init__.py is empty.
bar/baz.py: worked = True

foo/__init__.py:

import sys

# make sure bar is in sys.modules
import bar
# link this module to bar
sys.modules[__name__] = sys.modules['bar']

# Or simply
sys.modules[__name__] = __import__('bar')

foo_bar.py:

import foo.baz

assert(hasattr(foo, 'baz') and hasattr(foo.baz, 'worked'))
assert(foo.baz.worked)

import bar
assert(foo is bar)
ThinkChaos
  • 1,803
  • 2
  • 13
  • 21
  • 1
    It not works properly. If module have submodules then they are not the same. – Grzegorz Bokota Jul 03 '19 at 14:27
  • 1
    For a reliable solution that includes submodules, you would need to explicitly add aliases for each submodule to sys.modules as well. This works (I've tried it on Python 3.6/3.7 so even recent versions), and successfully avoids the subtle bug where you end up with multiple instances of the same module with different names (which can cause nightmares if you have per-module static state, or isinstance() checks which get confused if there are multiple versions of the same class with different names). Works better than the MetaPathFinder approach. – Ben Spiller Nov 14 '19 at 23:19
  • I wonder if there is any solution that does not involve creation of a separate (`foo`) directory. –  Apr 20 '21 at 19:44
6

This answer work with submodules:

import sys
import os
from importlib.abc import MetaPathFinder, Loader
import importlib
from MainModule.SubModule import *

class MyLoader(Loader):
    def module_repr(self, module):
        return repr(module)

    def load_module(self, fullname):
        old_name = fullname
        names = fullname.split(".")
        names[1] = "SubModule"
        fullname = ".".join(names)
        module = importlib.import_module(fullname)
        sys.modules[old_name] = module
        return module


class MyImport(MetaPathFinder):
    def find_module(self, fullname, path=None):
        names = fullname.split(".")
        if len(names) >= 2 and names[0] == "Module" and names[1] == "LegacySubModule":
            return MyLoader()


sys.meta_path.append(MyImport())
Grzegorz Bokota
  • 1,736
  • 11
  • 19
  • 1
    Though a little contrived, this example really shows how to override importing and is simply better than the whole `importlib.abc` obnoxious documentation. Thank you! – λuser Apr 04 '21 at 20:25
  • I had an issue, when I tried to load a pickle which required a module that was refactored to a new name. This answer worked perfectly to remap the old imports to the new ones automatically – Alexey S. Larionov Apr 21 '22 at 20:31