-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
  • 119,623
  • 25
  • 170
  • 301

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
  • 510,633
  • 85
  • 743
  • 889
jsbueno
  • 99,910
  • 10
  • 151
  • 209