Andrew Tomazos
Andrew Tomazos

Reputation: 68588

Programmatic import in Python 3?

Say I have a directory foo that contains some .py files.

Each of these python files has a class Bar defined.

Say there were three .py files called baz.py, qux.py and quux.py - then to get the bar classes I could write:

import foo.baz
import foo.qux
import foo.quux

bars = [
    foo.baz.Bar,
    foo.qux.Bar,
    foo.quux.Bar,
]

However, rather than list out all the imports like this and all the Bar names, I want to populate bars programmatically.

That is, I want to write some code to populate bars that doesn't change as .py files are added or removed from the foo directory.

One way to do this might be to use Path('foo').iterdir() to list the .py files, but then how do I do a programmatic import of a string and name its Bar class?

Or is there some totally different approach to achieve this?

bars = []
for py_file in the directory foo:
   import foo.py_file  ???
   bars.append(foo.py_file.Bar)  ???

Upvotes: 2

Views: 298

Answers (2)

tdelaney
tdelaney

Reputation: 77337

You can use importlib to import the modules dynamically. But you need to find them first. If you include foo.__init__.py in the source, then then foo.__file__ will be that name. You can use that as the base of your glob, but you need to make sure you don't try to import __init__.py itself. This technique is nice because you don't have to worry about another bit of source importing the same module name but getting a different module in memory.

from pathlib import Path
import importlib
import foo

def no_magic(paths):
    for path in paths:
        if not path.name.startswith("_"):
            yield path

bars = []
for py in no_magic(Path(foo.__file__).resolve().parent.glob("*.py")):
    mod = importlib.import_module(f"foo.{py.stem}", foo)
    bars.append(mod.Bar)

print(bars)

Upvotes: 0

Tibebes. M
Tibebes. M

Reputation: 7538

Here is one approach using glob module to match the pathnames of the modules we would like to import and importlib's SourceFileLoader to import.

from glob import glob
from importlib.machinery import SourceFileLoader

modules = glob('foo/*.py')

bars = list(map(lambda pathname: SourceFileLoader(".", pathname).load_module().Bar, modules))

Upvotes: 1

Related Questions