root/ipython/branches/saw/sandbox/tconfig/tconfignogui.py

Revision 2527, 6.4 kB (checked in by fperez, 3 years ago)

Put all of tconfig into its own directory.

Line 
1 """Mix of Traits and ConfigObj.
2 """
3
4 # Stdlib imports
5 from inspect import isclass
6 from pprint import pprint
7
8 # External imports
9 from enthought.traits.api import HasTraits, HasStrictTraits, MetaHasTraits, \
10      Trait, TraitError, CInt, CFloat, String, ReadOnly
11
12 from configobj import ConfigObj
13
14 # Code begins
15
16 # Utility functions
17 def get_traits(obj):
18     skip = set(['trait_added','trait_modified'])
19     return [k for k in obj.__class_traits__ if k not in skip]
20
21 # Alias for naming consistency with configobj conventions
22 get_scalars = get_traits
23
24 def get_sections(obj,sectionClass):
25     return [(n,v) for (n,v) in obj.__dict__.iteritems()
26             if isclass(v) and issubclass(v,sectionClass)]
27
28
29 # Metaclass hack to avoid exposing ANY GUI dependency, as suggested by R. Kern
30 # and others on the Enthought-dev list
31 #class MetaHasStrictTraits(MetaHasTraits, HasStrictTraits):
32 class MetaHasTraitsNoGUI(MetaHasTraits):
33    """ This merges the two metaclasses to avoid conflicts when we later
34    multiply inherit from two classes that use each of them.
35    """
36
37    def __init__(cls,class_name, bases, class_dict ):
38        print '*'*80
39        print 'in __init__'
40        print 'cls:'
41        #pprint(dir(cls))
42        print class_name
43        #print bases
44        print '-'*80
45        print 'class_dict:'
46        pprint(class_dict)
47        print '.'*80
48        pprint(dict(cls.__dict__))
49        print 'edit:',cls.edit_traits
50
51        #del class_dict['edit_traits']
52
53        super(MetaHasTraitsNoGUI,cls).__init__(class_name,bases,class_dict)
54
55
56 class HasStrictTraitsNoGUI(HasStrictTraits):
57    """ This class enables any Python class derived from it to behave like
58    HasStrictTraits, but it disables all GUI-related Traits support (traitsui),
59    so that instances can be used outside of any GUI toolkit.
60    """
61
62    #__metaclass__ = MetaHasTraitsNoGUI
63
64    # Disable GUI support
65    def edit_traits(self,*args,**kw):
66        print "I'm sorry Dave, I'm afraid I can't do that."
67
68    configure_traits = edit_traits
69
70
71 # Main TConfig class and supporting exceptions
72 class TConfigError(Exception): pass
73
74 class TConfigInvalidKeyError(TConfigError): pass
75
76 class TConfig(HasStrictTraitsNoGUI):
77     """A class representing configuration objects.
78
79     Note: this class should NOT have any traits itself, since the actual traits
80     will be declared by subclasses.  This class is meant to ONLY declare the
81     necessary initialization/validation methods.  """
82    
83     def __init__(self,config):
84         """Makes a Traited config object out of a ConfigObj instance
85         """
86         # Validate the set of keys
87         my_traits = set(get_traits(self))
88         conf_keys = set(config.keys())
89         invalid_keys = conf_keys - my_traits
90         if invalid_keys:
91             raise TConfigInvalidKeyError("Invalid keys: %s" % invalid_keys)
92
93         # Now set the traits based on the config
94         try:
95             for k in conf_keys:
96                 setattr(self,k,config[k])
97         except TraitError,e:
98             t = self.__class_traits__[k]
99             msg = "Bad key,value pair given: %s -> %s\n" % (k,config[k])
100             msg += "Expected type: %s" % t.handler.info()
101             raise TConfigError(msg)
102
103 # Example simple configuration
104 class MyConfig(TConfig):
105     n = CInt
106     x = CFloat
107     s = String
108
109 class ReadOnlyTConfig(HasStrictTraitsNoGUI):
110     def __init__(self,tconf):
111         tctraits = get_traits(tconf)
112         for t in tctraits:
113             self.add_trait(t,ReadOnly)
114             setattr(self,t,getattr(tconf,t))
115    
116 # Example app using this
117 class App0(object):
118     def __init__(self,init_only_config,init_config):
119         self.rcx = ReadOnlyTConfig(MyConfig(init_only_config))
120         self.rc = MyConfig(init_config)
121
122 # More complex application config
123 #class TConfig2(HasStrictTraitsNoGUI):
124 class TConfig2(HasTraits):
125     """A class representing configuration objects.
126
127     Note: this class should NOT have any traits itself, since the actual traits
128     will be declared by subclasses.  This class is meant to ONLY declare the
129     necessary initialization/validation methods.  """
130    
131     def __init__(self,config):
132         """Makes a Traited config object out of a ConfigObj instance
133         """
134         # Validate the set of scalars ...
135         my_scalars = set(get_scalars(self))
136         cf_scalars = set(config.scalars)
137         invalid_scalars = cf_scalars - my_scalars
138         if invalid_scalars:
139             raise TConfigInvalidKeyError("Invalid keys: %s" % invalid_scalars)
140         # ... and sections
141         my_sections1 = get_sections(self.__class__,TConfig2)
142         my_sections = set([n for n,v in my_sections1])
143         cf_sections = set(config.sections)
144         invalid_sections = cf_sections - my_sections
145         if invalid_sections:
146             raise TConfigInvalidKeyError("Invalid sections: %s" %
147                                          invalid_sections)
148
149         # Now set the traits based on the config
150         try:
151             for k in cf_scalars:
152                 setattr(self,k,config[k])
153         except TraitError,e:
154             t = self.__class_traits__[k]
155             msg = "Bad key,value pair given: %s -> %s\n" % (k,config[k])
156             msg += "Expected type: %s" % t.handler.info()
157             raise TConfigError(msg)
158
159         # And build subsections
160         for s,v in my_sections1:
161             setattr(self,s,v(config[s]))
162
163
164
165 class ReadOnlyTConfig2:
166     pass
167
168 class AppConfig(TConfig2):
169
170     m = CInt
171    
172     class InitOnly(TConfig2,ReadOnlyTConfig2):
173         n = CInt
174         x = CFloat
175
176     class Protocol(TConfig2):
177         ptype = String
178
179         class Handler(TConfig2):
180             key = String
181
182     class Machine(TConfig2):
183         ip = String
184         port = CInt
185
186 class App(object):
187     def __init__(self,conf_filename):
188         conf = ConfigObj(conf_filename)
189         self.rc = AppConfig(conf)
190
191         #self.rc = mkConfig(AppConfig,conf_filename)
192
193
194 # Testing
195 if __name__ == '__main__':
196
197     # Normal tests
198     conf0 = dict(n='3',x='4.15',s='Python')
199     t1 = MyConfig(conf0)
200    
201     fname = 'tconfig1.ini'
202     conf1 = ConfigObj(fname)
203     t2 = MyConfig(conf1)
204
205     app0 = App0(conf0,conf1)
206
207
208     fname2 = 'tconfig2.conf'
209     conf2 = ConfigObj(fname2)
210     app1 = App(fname2)
211
212     # A few exception-raising tests, turn this later into a doctest that
213     # actually runs them, once we settle the exception hirerarchy and format
214     if 0:
215         bad_config = dict(n='3',bad=10)
216         tb1 = MyConfig(bad_config)
217
218         bad_config2 = dict(n='3',x='Not a number',s='Python')
219         tb2 = MyConfig(bad_config2)
Note: See TracBrowser for help on using the browser.