-2

I would like to simplify as much as possible the syntax and the number of lines, so I was wondering why the following statement is an erro:

from os.path.join(path1,path2,filename) import *

the os.path.join seems not to run with the import statement.

Is there any workaround?

martineau
  • 112,593
  • 23
  • 157
  • 280

1 Answers1

2

Python's import statement needs a "module name", not a file path to work. The way to import using a variable is to use the importlib.import_module built-in function. You have, of course, to use your filesystem "path" directory names to package names, and the top most of which must be "visible" in the PythonPath. The [:-3] part is just there to strip the .py part, but you can just use the modulename without the ".py" to startwith.

from importlib import import_module

module = importmodule('.'.join([path1, path2, filename[:-3]])
for attr in dir(module):
    globals()[attr] = getattr(module, attr)

However, as you see, you will populate your namespace with names you don't know which they are, and thus, you can't have program code using those names - what makes it kind of pointless. It may be more useful to simply create a new dictionary with the attributes of your imported module:

namespace = {}

for attr in dir(module):
    namespace[attr] = getattr(module, attr)

so you can introspect what was imported and call/use those values.

Now, if your "path1", and "path2" are not a package, and you really want to just import module from an arbitrary location, what is needed is to have the location inserted in the Pythonpath bedore calling import_module. The Pythonpath, however, is a simple list located at sys.path - just insert the full-folder path there (and pop it later if you want):


def import_path(path1, path2, module_name, namespace=None):
    import sys
    from importlib import import_module
    from pathlib import Path # new code should use this instead of "os.path" stuff
    path = Path(path1).joinpath(path2)
    name = module_name[-3:] if module_name.endswith(".py") else module_name
    try:
        sys.path.insert(0, str(path))
        module = import_module(name)
    finally:
        sys.path.remove(str(path))
    if namespace is None:
        namespace = sys._getframe().f_back.f_globals()
    for name in dir(module):
        namespace[name] = getattr(module, name)


(One can also use directly the __import__ function without needing to to use importlib.import_module, but its use is not advised in the documentation. Among other things when using the single argument form __import__("path1.path2.module") will return path1 not module.)

deceze
  • 491,798
  • 79
  • 706
  • 853
jsbueno
  • 86,446
  • 9
  • 131
  • 182