Alphadelta14
Alphadelta14

Reputation: 2923

Python import from module variable

I have a package that contains a module with compatible imports. However, I cannot import from the exposed modules directly; I have to use slightly more inconvenient workarounds.

Inside common/compat.py, I have this:

import bcrypt

In main.py, this does not work:

from common.compat.bcrypt import hashpw

with the error:

Traceback (most recent call last):
  File "main.py", line 2, in <module>
    from common.compat.bcrypt import hashpw
ImportError: No module named bcrypt

However, this works fine:

from common.compat import bcrypt
# now bcrypt.hashpw works fine

Is there any way to make the first one work properly without having to do the second solution? I would prefer from imports for quite a few classes and methods.

Upvotes: 2

Views: 926

Answers (2)

Silas Ray
Silas Ray

Reputation: 26160

The problem is that bcrypt is not actually a submodule of common.compat; the module object is actually a member attribute of the common.compat module object with the name bcrypt within common.compat's namespace. As a consequence, if you want to be able to import bcrypt as if it is a submodule of common.compat as opposed to a value from its namespace, you need to monkey around a bit. You could create a dummy bcrypt module within your package structure then from bcrypt import * inside it to alias it, but probably the better way would be to directly modify sys.modules in compat.py.

import sys
import bcrypt

sys.modules['common.compat.bcrypt'] = bcrypt

As commenters have noted though, modifying sys.modules is dangerous. I'm inferring from the fact that you call your package common.compat that you are creating some sort of compatibility module that implements fallback logic in case some dependency is not available. Instead of hacking sys.modules, you'll likely be better served creating a common.compat.crypto module that looks something like

try:
    from bcrypt import hashpw
except ImportError:
    # Alternative import, hand-written alternative,
    # or function that raises NotImplementedError here

Then in your main.py, just from common.compat.crypto import hashpw.

Upvotes: 1

Diane M
Diane M

Reputation: 1512

Would the following be what you are looking for ?

from common.compat import bcrypt.hashpw

However, it seems a bad idea to build depedencies in such way, because you don't seem to need that compat.py even exist to get your bcrypt module. Thus I would rather do one of the following :

  1. Import bcrypt.hashpw in main.py, as a direct dependency
  2. Import bcrypt from the common/compat.py script like you suggest in your last snippet
  3. Do something with hashpw in common/compat.py (e.g. subclassing or wrapping), and import the subclass/wrapper from compat.py in main.py

Upvotes: 0

Related Questions