ClassRegistry.py 4.17 KB
Newer Older
eric pellegrini's avatar
eric pellegrini committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import abc

class _Meta(type):
    '''
    Metaclass that allows to use the __getitem__ method at a class level for the class that has been built.
     
    The class that uses this metaclass must define a class attribute named _registry that will be used
    by the __getitem__ method.
    '''
         
    def __getitem__(self, item):
        """
        Returns a given item stored in the class registry
        """
         
        return self._registry[item]
    
class ClassRegistry(abc.ABCMeta):
    '''
    Metaclass that registers the subclasses of bases classes.

    The internal registry is defined as a nested dictionary whose keys 
    are the |type| class attribute of the base classes and values another dictionary 
    whose keys are the |type| class attribute of the subclasses and values are the corresponding 
    class instances.

eric pellegrini's avatar
eric pellegrini committed
27
    Hence any base or child class that does not define |type| class attribute will not be resgistered.
eric pellegrini's avatar
eric pellegrini committed
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    '''
    
    __metaclass__ = _Meta

    __interfaces = []

    _registry = {}
                
    def __init__(self, name, bases, namespace):
        '''
        Constructor of a class metaclassed by ClassFactory
        
        :param name: the name of the class to be built by this metaclass
        :param bases: the base classes of the class to be built by this metaclass
        :param namespace: the attributes and methods of the class to be built by this metaclass
        '''
        
        super(ClassRegistry, self).__init__(name, bases, namespace)
        
        # Get the typ of the class
        typ = getattr(self, 'type', None)
                
        if typ is None:
            return

        metaClass = namespace.get("__metaclass__", None)
                
        if metaClass is ClassRegistry:
            ClassRegistry.__interfaces.append(self)
            ClassRegistry._registry[typ] = {}

        else:
                                
            for interface in ClassRegistry.__interfaces:
                
                if issubclass(self, interface):
                    ClassRegistry._registry[interface.type][typ] = self
                    break

    @classmethod
    def info(cls, interface):
        '''
        Returns informations about the subclasses of a given base class  stored in the registry.
        
        :param cls: the ClassRegsitry instance
        :param interface: the name of base class of whom information about its subclasses is requested
        '''
                
        if not cls._registry.has_key(interface):
            return "The interface " + interface + " is not registered"

        import inspect
        import os

        # Dictionnay whose keys are the package names and values and list of (job name, job path) stored in the corresponding package. 
        packages = {}

        # Loop over the registry items.
        for k, v in cls._registry[interface].items():
            
            # Get the module corresponding to the job class.
            mod = inspect.getmodule(v)

            # The package hosting the module.
            modPackage = mod.__package__
        
            # The module file.
            modFilename = mod.__file__
        
            # If no package could be found, guess a name using the directory name of the module file.
            if modPackage is None:
                modPackage = os.path.split(os.path.dirname(modFilename))[1]
        
            # Update the packages dictionary.
            if packages.has_key(modPackage):
                packages[modPackage].append([k, v.__name__])
            else:
                packages[modPackage] = [[k, v.__name__]]
    
        contents = []
        
        # Print the contents of the packages dictionary.
        contents.append("="*130)
        contents.append("%-50s %-40s %-s" % ("Package", "Name", "Class"))
        contents.append("="*130)
        for k, v in sorted(packages.items()):
            for vv in sorted(v): 
                contents.append("%-50s %-40s %-s" % (k, vv[0], vv[1]))
            contents.append('-' * 130)
        
        contents = "\n".join(contents)
    
        return contents