How to find python class hierarchy across modules?

Viewed 475

How to find class parent and subclasses across different modules without running the code (static analysis)

Module Contains __init__.py and 4 files as below

Example first_file.py

class Parent(object):
    def __init__(self):
        pass

    def method1(self):
        print('parent')

Example second_file.py

from first_file import Parent
class Child(Parent):
    def __init__(self):
        pass

    def method1(self):
        print('child')

Example third_file.py

from first_file import Parent
class Child1(Parent):
    def __init__(self):
        pass

    def method1(self):
        print('child1')

Example fourth_file.py

from second_file import Child
class Child2(Child):
    def __init__(self):
        pass

    def method1(self):
        print('child2')

I want the way to list down the subclasses of parent given filename and class example

>>> findclasshierchay first_file.py --class Parent

and it will list down all subclasses with filenames

1 Answers

Here's a stab at using cls.mro().

Disclaimer, I don't know if you consider this to be running the code or not. It's not static analysis, but if you have no great side effects upon module load, it really doesn't do that much either.

file1.py

class File1:
    pass

file2.py

from file1 import File1

class File2(File1):
    pass

listhier.py

import sys
import copy
import os
from importlib import import_module
from importlib.util import find_spec as importlib_find


#shamelessly copied from django 

def import_string(dotted_path):
    """
    Import a dotted module path and return the attribute/class designated by the
    last name in the path. Raise ImportError if the import failed.
    """
    try:
        module_path, class_name = dotted_path.rsplit('.', 1)
    except ValueError as err:
        raise ImportError("%s doesn't look like a module path" % dotted_path) from err

    module = import_module(module_path)

    try:
        return getattr(module, class_name)
    except AttributeError as err:
        raise ImportError('Module "%s" does not define a "%s" attribute/class' % (
            module_path, class_name)
        ) from err


toload = sys.argv[1]

cls = import_string(toload)

for cls_ in cls.mro():
    print("%s.%s" % (cls_.__module__, cls_.__name__))

python listhier.py file2.File2

output:

file2.File2
file1.File1
builtins.object
Related