Greem666
Greem666

Reputation: 949

Dynamic import of class defined in a module

I have a module Class3.py, with the following code in it:

from common.Step import Step
from StageThree.Class2 import Class2

class Class3(Step):
    pass

I have a logic inside of my project, which will dynamically access this module, and read it in:

module = importlib.import_module(module_name, package_path)

Now, the module variable will hold a dictionary of all information about this module, but I am interested only in grabbing a handle to Class3 from it, for later dynamic instantiation. I sort through that dict with the following code:

dict([(name, cls) for name, cls in module.__dict__.items() if isinstance(cls, type)])

Which yields this result:

{'Step': <class 'common.Step.Step'>, 'Class2': <class 'StageThree.Class2.Class2'>, 'Class3': <class 'StageThree.Class3.Class3'>}

While I know now what classes are present in that module, this still does not help me, because I just want to instantiate a class which was defined in this module (Class3), not imported like Step or Class2.

Any ideas on how I can make Python actually grab only a handle to Class3 in this situation?

UPDATE:

With some ingenuity, I have scrambled together a code which interrogates module for its path, and then reads in file, looks for a line which includes statement "class some_name(" and grabs the name from there. Dirty, but works for now.

available_classes = dict([(name, cls) for name, cls in module.__dict__.items() if isinstance(cls, type)])

with open(module.__file__) as f: 
    declared_classes_names = re.findall(r"class (\w+)\(", " ".join(f.read().splitlines()))

output = []
for class_name in declared_class_names:
    output.append(available_classes[class_name])

Would still be interested to see, if there is any valid way of doing it without brute force approach like above.

Upvotes: 0

Views: 204

Answers (1)

Maurice Meyer
Maurice Meyer

Reputation: 18106

You can check the __module__'s name as well:

import importlib


class ModuleInspector(object):
    def __init__(self):
        self.module = None

    def getClassesInModule(self, localOnly=True):
        """
        localOnly, returns only locally defined classes in that particular module.
        """
        _classes = {}
        for k, v in self.module.__dict__.items():
            if isinstance(v, type):
                if not localOnly or v.__module__ == self.module.__name__:
                    _classes[k] = v
        return _classes

    def importFromFile(self, fpath, name=None):
        try:
            self.module = importlib.import_module(name, fpath)
        except:
            traceback.print_exc()


if __name__ == '__main__':
    i = ModuleInspector()
    i.importFromFile('/tmp/class3.py', 'class3')
    available_classes = i.getClassesInModule(localOnly=False)
    declared_classes_names = i.getClassesInModule()

    print(available_classes)
    print(declared_classes_names)

Output:

{'Queue': <class 'queue.Queue'>, 'scheduler': <class 'sched.scheduler'>, 'Class3': <class 'class3.Class3'>}
{'Class3': <class 'class3.Class3'>}

Upvotes: 2

Related Questions