source: tailor/vcpx/config.py @ 1439

Revision 1439, 9.9 KB checked in by lele@…, 6 years ago (diff)

Fix #82: put all loggers at DEBUG level when --debug is given

RevLine 
[463]1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- Configuration bits
3# :Creato:   sab 30 lug 2005 20:51:28 CEST
4# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
5# :Licenza:  GNU General Public License
6#
7
8"""
9Handle the configuration details.
10"""
11
12__docformat__ = 'reStructuredText'
13
[949]14from cStringIO import StringIO
[622]15from ConfigParser import SafeConfigParser, NoSectionError, DEFAULTSECT
[1178]16from vcpx import TailorException
[463]17
[1178]18
19class ConfigurationError(TailorException):
[478]20    """Configuration error"""
[463]21
[1180]22
[940]23LOGGING_SUPER_SECTION = '[[logging]]'
24BASIC_LOGGING_CONFIG = """\
25[formatters]
26keys = console
27
28[formatter_console]
29format =  %(asctime)s [%(levelname).1s] %(message)s
30datefmt = %H:%M:%S
31
32[loggers]
33keys = root
34
35[logger_root]
36level = INFO
37handlers = console
38
39[handlers]
40keys = console
41
42[handler_console]
43class = StreamHandler
44formatter = console
45args = (sys.stdout,)
46level = INFO
47"""
48
[1180]49
[463]50class Config(SafeConfigParser):
[940]51    '''
[463]52    Syntactic sugar around standard ConfigParser, for easier access to
53    the configuration. To access any single project use the configuration
54    as a dictionary.
55
56    The file may be a full fledged Python script, starting
[938]57    with the usual ``"#!..."`` notation: in this case, it gets evaluated and
[463]58    its documentation becomes the actual configuration, while the functions
[938]59    it defines may be referenced by the `before-commit` and `after-commit`
[463]60    slots.
[940]61
62    This is where the logging system gets initialized, possibly merging a
63    logging specific configuration section, introduced by a *supersection*
64    ``[[logging]]``.
65    '''
[463]66
67    def __init__(self, fp, defaults):
[648]68        SafeConfigParser.__init__(self)
[463]69        self.namespace = {}
[940]70
71        loggingcfg = None
[491]72        if fp:
73            if fp.read(2) == '#!':
74                fp.seek(0)
75                exec fp.read() in globals(), self.namespace
[940]76                config = self.namespace['__doc__']
[491]77            else:
78                fp.seek(0)
[940]79                config = fp.read()
80
81            # Look for a [[logging]] separator, that introduce a
82            # standard logging  section
83            cfgs = config.split(LOGGING_SUPER_SECTION)
84            if len(cfgs) == 2:
85                tailorcfg, loggingcfg = cfgs
86            else:
87                tailorcfg = cfgs[0]
88
89            self.readfp(StringIO(tailorcfg))
90
[648]91        # Override the defaults with the command line options
92        if defaults:
93            self._defaults.update(defaults)
[463]94
[949]95        self._setupLogging(loggingcfg and loggingcfg or BASIC_LOGGING_CONFIG)
96
97    def _setupLogging(self, config):
98        """
99        Tailor own's approach at file based logging configuration.
100        """
101
102        import logging, logging.handlers
103
104        cp = SafeConfigParser()
105        cp.readfp(StringIO(config), self._defaults)
106
107        #first, do the formatters...
108        flist = cp.get("formatters", "keys")
109        if flist:
110            flist = flist.split(',')
111            formatters = {}
112            for form in flist:
113                sectname = "formatter_%s" % form
114                opts = cp.options(sectname)
115                if "format" in opts:
116                    fs = cp.get(sectname, "format", 1)
117                else:
118                    fs = None
119                if "datefmt" in opts:
120                    dfs = cp.get(sectname, "datefmt", 1)
121                else:
122                    dfs = None
123                f = logging.Formatter(fs, dfs)
124                formatters[form] = f
125        #next, do the handlers...
[1439]126        dbglevel = self._defaults.get('debug', False) and 'DEBUG' or None
[949]127        try:
128            hlist = cp.get("handlers", "keys")
129            if hlist:
130                handlers = {}
131                fixups = [] #for inter-handler references
132                for hand in hlist.split(','):
133                    try:
134                        sectname = "handler_%s" % hand
135                        klass = cp.get(sectname, "class")
136                        opts = cp.options(sectname)
137                        if "formatter" in opts:
138                            fmt = cp.get(sectname, "formatter")
139                        else:
140                            fmt = None
141                        klass = eval(klass, vars(logging))
142                        args = cp.get(sectname, "args")
143                        args = eval(args, vars(logging))
144                        h = apply(klass, args)
[1439]145                        if dbglevel:
146                            level = dbglevel
147                        elif "level" in opts:
[949]148                            level = cp.get(sectname, "level")
[1439]149                        else:
150                            level = None
151                        if level:
[949]152                            h.setLevel(logging._levelNames[level])
153                        if fmt:
154                            h.setFormatter(formatters[fmt])
155                        #temporary hack for FileHandler and MemoryHandler.
156                        if klass == logging.handlers.MemoryHandler:
157                            if "target" in opts:
158                                target = cp.get(sectname,"target")
159                            else:
160                                target = ""
161                            if len(target):
162                                # the target handler may not be loaded
163                                # yet, so keep for later...
164                                fixups.append((h, target))
165                        handlers[hand] = h
166                    except:
167                        #if an error occurs when instantiating a
168                        #handler, too bad this could happen
169                        #e.g. because of lack of privileges
170                        raise #pass
171                #now all handlers are loaded, fixup inter-handler references...
172                for h,t in fixups:
173                    h.setTarget(handlers[t])
174            #at last, the loggers...first the root...
175            llist = cp.get("loggers", "keys")
176            if llist:
177                llist = llist.split(',')
178            llist.remove("root")
179            sectname = "logger_root"
180            root = logging.root
181            opts = cp.options(sectname)
[1439]182            if dbglevel:
183                level = dbglevel
184            elif "level" in opts:
[949]185                level = cp.get(sectname, "level")
[1439]186            else:
187                level = None
188            if level:
[949]189                root.setLevel(logging._levelNames[level])
190            for h in root.handlers[:]:
191                root.removeHandler(h)
192            hlist = cp.get(sectname, "handlers")
193            if hlist:
194                for h in hlist.split(','):
195                    root.addHandler(handlers[h])
196            #and now the others...
197            for log in llist:
198                sectname = "logger_%s" % log
199                qn = cp.get(sectname, "qualname", log)
200                opts = cp.options(sectname)
201                if "propagate" in opts:
202                    propagate = cp.getint(sectname, "propagate")
203                else:
204                    propagate = 1
205                logger = logging.getLogger(qn)
[1439]206                if dbglevel:
207                    level = dbglevel
208                elif "level" in opts:
[949]209                    level = cp.get(sectname, "level")
[1439]210                else:
211                    level = None
212                if level:
[949]213                    logger.setLevel(logging._levelNames[level])
214                for h in logger.handlers[:]:
215                    logger.removeHandler(h)
216                logger.propagate = propagate
217                logger.disabled = 0
218                hlist = cp.get(sectname, "handlers")
219                if hlist:
220                    for h in hlist.split(','):
221                        logger.addHandler(handlers[h])
222        except:
223            from sys import exc_info, stderr
224            from traceback import print_exception
225            ei = exc_info()
226            print_exception(ei[0], ei[1], ei[2], None, stderr)
227            del ei
[940]228
[463]229    def projects(self):
230        """
231        Return either the default projects or all the projects in the
232        in the configuration.
233        """
234
235        defaultp = self.getTuple('DEFAULT', 'projects')
236        return defaultp or [s for s in self.sections() if not ':' in s]
237
[619]238    def get(self, section, option, default=None, raw=False, vars=None):
239        """Get an option value for a given section or the default value.
240
241        All % interpolations are expanded in the return values, based on the
242        defaults passed into the constructor, unless the optional argument
[708]243        `raw` is true.  Additional substitutions may be provided using the
244        `vars` argument, which must be a dictionary whose contents overrides
[619]245        any pre-existing defaults, but not those in the given section.
246
247        The section DEFAULT is special.
[463]248        """
[612]249
[619]250        # Reimplement parent behaviour, that uses `vars` to override even
251        # the value in the specific section... Overriding the defaults
252        # seems a better idea
253
254        d = self._defaults.copy()
255        # Update with the entry specific variables
256        if vars is not None:
257            d.update(vars)
258        try:
259            d.update(self._sections[section])
260        except KeyError:
[1283]261            pass
[619]262        option = self.optionxform(option)
263        try:
264            value = d[option]
265        except KeyError:
266            value = default
267
268        if not raw:
[955]269            value = self._interpolate(section, option, str(value), d)
[619]270
271        if value == 'None':
[463]272            return default
[619]273        elif value == 'True':
274            return True
275        elif value == 'False':
276            return False
277        else:
278            return value
[463]279
280    def getTuple(self, section, option, default=None):
281        """
282        Parse the requested option as a tuple, if its value starts with
283        an open bracket, otherwise consider the value a single item
284        tuple.
285        """
286
287        value = self.get(section, option, default)
288        if value:
289            if value.startswith('('):
290                items = value.strip()[1:-1]
291            else:
292                items = value
293            return [i.strip() for i in items.split(',')]
294        else:
295            return []
Note: See TracBrowser for help on using the repository browser.