3

I have a system that collects all classes that derive from certain base classes and stores them in a dictionary. I want to avoid having to specify which classes are available (I would like to discover them programatically), so have used a from ModuleName import * statement. The user is then directed to place all tests to be collected in the ModuleName module. However, I cannot find a way to programatically determine what symbols were imported with that import statement. I have tried using dir() and __dict__ as indicated in the following example, but to no avail. How does one programatically find symbols imported in this manner (with import *)? I am unable to find them with the above methods.

testTypeFigureOuterrer.py:

from testType1 import *
from testType2 import *

class TestFigureOuterrer(object):

    def __init__(self):
        self.existingTests = {'type1':{},'type2':{}}

    def findAndSortTests(self):

        for symbol in dir(): # Also tried: dir(self) and __dict__
            try:
                thing = self.__getattribute__(symbol)
            except AttributeError:
                continue
            if issubclass(thing,TestType1):
                self.existingTests['type1'].update( dict(symbol,thing) )
            elif issubclass(thing,TestType3):
                self.existingTests['type2'].update( dict(symbol,thing) )
            else:
                continue

if __name__ == "__main__":
    testFigureOuterrer = TestFigureOuterrer()
    testFigureOuterrer.findAndSortTests()

testType1.py:

class TestType1(object):
    pass

class TestA(TestType1):
    pass

class TestB(TestType1):
    pass

testType2.py:

class TestType2:
    pass

class TestC(TestType2):
    pass

class TestD(TestType2):
    pass
tintedFrantic
  • 177
  • 1
  • 9

3 Answers3

5

Since you know the imports yourself, you should just import the module manually again, and then check the contents of the module. If an __all__ property is defined, its contents are imported as names when you do from module import *. Otherwise, just use all its members:

def getImportedNames (module):
    names = module.__all__ if hasattr(module, '__all__') else dir(module)
    return [name for name in names if not name.startswith('_')]

This has the benefit that you do not need to go through the globals, and filter everything out. And since you know the modules you import from at design time, you can also check them directly.

from testType1 import *
from testType2 import *

import testType1, testType2

print(getImportedNames(testType1))
print(getImportedNames(testType2))

Alternatively, you can also look up the module by its module name from sys.modules, so you don’t actually need the extra import:

import sys
def getImportedNames (moduleName):
    module = sys.modules[moduleName]
    names = module.__all__ if hasattr(module, '__all__') else dir(module)
    return [name for name in names if not name.startswith('_')]
print(getImportedNames('testType1'))
print(getImportedNames('testType2'))
poke
  • 369,085
  • 72
  • 557
  • 602
  • Thank you for the answer. This worked. It just required more code than the answer submitted by @GJStein – tintedFrantic Jul 20 '15 at 15:40
  • @tintedFrantic Sure, my solution checks for all exported names, not only types, but if you’re interested in classes only and they are all exported, then you’re good to go with the shorter way :) – poke Jul 20 '15 at 16:18
3

Take a look at this SO answer, which describes how to determine the name of loaded classes, you can get the name of all the classes defined within the context of the module.

import sys, inspect
clsmembers = inspect.getmembers(sys.modules['testType1'], inspect.isclass)

which is now defined as

[('TestA', testType1.TestA),
 ('TestB', testType1.TestB),
 ('TestType1', testType1.TestType1)]

You can also replace testType1 with __name__ when you're within the function of interest.

Community
  • 1
  • 1
GJStein
  • 648
  • 5
  • 13
0

Don't use the * form of import. This dumps the imported names into your script's global namespace. Not only could they clobber some important bit of data by using the same name, you don't have any easy way to fish out the names you just imported. (Easiest way is probably to take a snapshot of globals().keys() before and after.)

Instead, import just the module:

import testType1
import testType2

Now you can easily get a list of what's in each module:

tests = dir(testType1)

And access each using getattr() on the module object:

for testname in tests:
    test = getattr(testType1, testname)
    if callable(test):
        # do something with it
kindall
  • 178,883
  • 35
  • 278
  • 309
  • *“[…] you don't have any easy way to fish out the names you just imported”* – And exactly that is what OP is trying to do, finding out which names got imported that way. – poke Jul 17 '15 at 11:51
  • Yes, which is why it's better to *not do that.* – kindall Jul 17 '15 at 18:41