1

Suppose you have the following structure in your src folder:

conf.py
./drivers
    mod1.py --> contains mod1Class
    mod2.py --> contains mod2Class

What I'd like to have is a snippet of code in conf.py to automatically instantiate the classes in mod*.py so that if one day I'll add mod3.py --> mod3Class this will be automatically instantiated in conf.py without adding any line of code.

I tried, without success:

from drivers import *

but I'm not able to import, I receive a NameError. So I'm stuck at the very first step. Also suppose I'm able to perform the import successfully, how can I do:

mod1Class_instance = mod1.mod1Class() (in a cycle, one instance for every file in drivers)

in an automatic way? I cannot use strings to make the instance of a class so I cannot get the names of the files in drivers and use strings. What's the right way to do this operation?

Thanks

Luigi Tiburzi
  • 4,265
  • 7
  • 32
  • 43

2 Answers2

1

Maybe, it's what you need:

from types import ModuleType

import drivers

for driver_module in dir(drivers):
    if not isinstance(driver_module, ModuleType):
        continue # not real module driver
    for cls in dir(driver_module):
        if not isinstance(cls, SomeBaseClass):
             continue # not real mod class
        # create new variable with name as lower class name
        locals()[cls.__name__.lower()] = cls()

And, also, you should create __init__.py file in your drivers folder. This will mean that your folder is a python-module.

On the other hand, I recommend manually describe all imports. This simple approach makes your code more clear.

defuz
  • 26,721
  • 10
  • 38
  • 60
  • 1
    This does not work, submodules are not listed in ``dir(drivers)`` unless they are imported in ``drivers/__init__.py``. – Artur Gaspar Dec 20 '12 at 18:07
  • It is not working, as said by @Artur when I type dir(drivers) the modules are not listed, only __doc__, __name__ and a few others are listed but not my mod*.py modules. This is the point I'm stuck with and the one I can't understand. – Luigi Tiburzi Dec 21 '12 at 09:06
0

This loads classes in modules in a directory drivers, which is in the same directory of the current module, and does not need to make drivers a package:

from collections import defaultdict
import os
import pkgutil


def getclasses(module):
    """Get classes from a driver module."""
    # I'd recommend another way to find classes; for example, in defuz's 
    # answer, classes in driver modules would have one base class.
    try:
        yield getattr(module, module.__name__ + "Class")
    except AttributeError:
        pass

instances = defaultdict(list)
drivers_dir = os.path.join(os.path.dirname(__file__), 'drivers')
for module_loader, name, ispkg in pkgutil.iter_modules([drivers_dir]):
    module = module_loader.find_module(name).load_module(name)
    for cls in getclasses(module):
        # You might want to use the name of the module as a key instead of the  
        # module object, or, perhaps, to append all instances to a same list.
        instances[module].append(cls())

# I'd recommend not putting instances in the module namespace, 
# and just leaving them in that dictionary.
for mod_instances in instances.values():
    for instance in mod_instances:
        locals()[type(instance).__name__ + "_instance"] = instance
Artur Gaspar
  • 4,407
  • 1
  • 26
  • 28
  • I tried to run your standalone program both in ./drivers and ../drivers. As far as I have understand I should be able to run for example print mod1Class_instance.SomeAttribute (at the end of your code) and get the attribute but I'm getting the error: NameError: name mod1Class_instance is not defined. I have the __init__.py in my folders. Why am I getting this? Am I using your code rightly? Thanks – Luigi Tiburzi Dec 21 '12 at 09:29
  • @Luigi Tiburzi It happens because you are using old-style classes; you can either make your classes new-style in ``drivers/mod*,py``, by making them inherit from ``object``, which I'd recommend more, or you can change, in the last line, ``type(instance).__name__ + "_instance"`` to ``instance.__class__.__name__ + "_instance"``, which I would not recommend at all. Also, I edited the answer to fix a bug, in the line before the last. – Artur Gaspar Dec 22 '12 at 02:04
  • @Luigi Tiburzi Also, there is no need for an ``__init__.py`` file in ``drivers/``. – Artur Gaspar Dec 22 '12 at 02:23
  • I appreciate very much your help but probably there's something I'm doing wrong... I copy your code in a file try.py in a folder which has the following path to access the folder drivers: ./drivers. I change the last local with classes (an empty dictionary I previously defined) and then I try to print the dictionary to see what' there but it is empty. ALso I try to print class_instance.ClassAttribute but I don't get any output. What am I doing wrong? Did you test your code? Is it working for you? Thanks again for the advice – Luigi Tiburzi Dec 22 '12 at 14:38
  • @Luigi Tiburzi I did test it and it worked as expected. Could you tell me the output of, after all other code, ``print(instances)`` and ``print(locals())``? Also, how are your classes named in the files in ``drivers/``? – Artur Gaspar Dec 23 '12 at 04:29
  • This is the output. sorry for the horrible formatting: {'defaultdict': , 'ispkg': False, 'pkgutil': , '__builtins__': , '__file__': 'prova.py', 'module': , '__package__': None, 'instances': defaultdict(, {}), '__name__': '__main__', 'getclasses': , 'module_loader': – Luigi Tiburzi Dec 23 '12 at 12:21
  • , 'os': , '__doc__': None, 'drivers_dir': 'drivers', 'name': 'ssh'} – Luigi Tiburzi Dec 23 '12 at 12:22
  • The script is launched from the dir src which has drivers as subdir and in drivers I have the following modules: ssh.py, apache2.py and others. Each module has a class with the same name of the module, e.g. ssh() with some attribute, e.g. REGEX = some regex. I can see the module in locals but whatever thing I try to do: import drivers, print drivers.ssh..ssh().REGEX or print ssh.ssh().REGEx or others I'm unable to print the REGEX field. I got NameError: name x is not defined. – Luigi Tiburzi Dec 23 '12 at 12:28
  • @Luigi Tiburzi ``getclasses`` assumes a class is named ``{module name}Class``, as your question states ("mod1.mod1Class"). If the names of your classes are the same of their respective modules (e.g. ``ssh.py`` has a ``ssh`` class), then you should change, in ``getclasses``, ``yield getattr(module, module.__name__ + "Class")`` to ``yield getattr(module, module.__name__)``. – Artur Gaspar Dec 27 '12 at 08:18
  • @Luigi Tiburzi Also, sorry for the late reply. My internet connection got really slow and SO would not load properly. – Artur Gaspar Dec 27 '12 at 08:18
  • Don't worry that's fine :-) – Luigi Tiburzi Dec 27 '12 at 08:20