HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

List all classes in a package directory

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
directorypackageallclasseslist

Problem

I have a package directory pkg with several classes that I would like to build into a convenient dict property.

The structure of pkg/ looks like:

pkg/base.py:

class _MyBase(object):
    pass


pkg/foo.py:

from .base import _MyBase

class Foo(_MyBase):
     pass


And in pkg/__init__.py, it is a bit clunky, but once pkg is imported, a all_my_base_classes dict is built with a key of the class name, and value of the class object. The classes are all subclasses of pkg.base._MyBase.

import os
import sys
import pkgutil

import base
# I don't want to import foo, bar, or whatever other file is in pkg/

all_my_base_classes = {}
pkg_dir = os.path.dirname(__file__)
for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]):
    exec('import ' + name)
    pkg_name = __name__ + '.' + name
    obj = sys.modules[pkg_name]
    for dir_name in dir(obj):
        if dir_name.startswith('_'):
            continue
        dir_obj = getattr(obj, dir_name)
        if issubclass(dir_obj, base._MyBase):
            all_my_base_classes[dir_name] = dir_obj


Running it from an interactive Python shell, one directory below pkg/:

>>> import pkg
>>> pkg.all_my_base_classes
{'Foo': }


So it works as expected, but pkg/__init__.py is pretty terrible looking. How can it be better?

Solution

Since the classes are all subclasses of _MyBase, they can be accessed via _MyBase.__subclasses__() after they have been imported:

for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]):
    importlib.import_module('.' + name, __package__)

all_my_base_classes = {cls.__name__: cls for cls in base._MyBase.__subclasses__()}


For importing the modules, I followed the advice of Nihathrael.

Code Snippets

for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]):
    importlib.import_module('.' + name, __package__)

all_my_base_classes = {cls.__name__: cls for cls in base._MyBase.__subclasses__()}

Context

StackExchange Code Review Q#70268, answer score: 16

Revisions (0)

No revisions yet.