'How do I dynamically import a python module containing imports to sibling packages?
I'm developing a command line tool in python which takes a source directory and build directory as parameters. The tool should walk through all directories in the source dir, find .py files and check for class members of a specific class instance.
For example:
$ my-cli-tool --source-dir app/ --build-dir docs/
With the following file structure:
MyProj
├── app
│ ├── foo
│ │ └── foo.py
│ └── bar
│ └── bar.py
└── docs
└─ ...
foo.py:
def foo_method():
pass
class foo:
my_member = MyClass()
bar.py:
from app.foo.foo import foo_method
class bar:
my_member = MyClass()
Results in:
No module named 'app'
And if I use relative imports instead it results in:
attempted relative import beyond top-level package
Expected result:
As the program is intended to be a pip package, I would like to make it as pain free as possible for the users to integrate my package. I would like this cli tool to be able to work out of the box with their preferred style of imports.
My code so far:
import inspect
import os
import sys
from importlib import util
from argparse import ArgumentParser
def dir_path(string):
if os.path.isdir(string):
return os.path.abspath(string)
else:
raise NotADirectoryError(string)
def import_module(file_name, file_path):
spec = util.spec_from_file_location(file_name, file_path)
module = util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def main(args):
parser = ArgumentParser(prog='my-cli-tool')
parser.add_argument('--source-dir', type=dir_path, action='store', default=os.getcwd())
parser.add_argument('--build-dir', type=dir_path, action='store', default=os.getcwd())
args = parser.parse_args(args)
for root, dirs, filenames in os.walk(args.source_dir):
for file_name in filenames:
if file_name.endswith('.py'):
file_path = os.path.join(root, file_name)
module = import_module(file_name, file_path)
classes = [obj[1] for obj in inspect.getmembers(module) if inspect.isclass(obj[1])]
result = []
for class_ in classes:
for i in inspect.getmembers(class_):
if isinstance(i[1], MyClass):
result.append(getattr(class_, i[0]))
return result # Then make something with the results....
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|