~ubuntu-branches/debian/jessie/cherrypy3/jessie

« back to all changes in this revision

Viewing changes to cherrypy/lib/reprconf.py

  • Committer: Package Import Robot
  • Author(s): Gustavo Noronha Silva, JCF Ploemen, Stéphane Graber, Gustavo Noronha
  • Date: 2012-01-06 10:13:27 UTC
  • mfrom: (1.1.4) (7.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20120106101327-smxnhguqs14ubl7e
Tags: 3.2.2-1
[ JCF Ploemen ]
* New upstream release (Closes: #571196).
* Bumped Standards-Version to 3.8.4 (no changes needed).
* Removing patch 02: no longer needed, incorporated upstream.
* Updating patch 00 to match release.
* Install cherryd man page via debian/manpages.
* debian/copyright:
  + Added notice for cherrypy/lib/httpauth.py.
  + Fixed years.
* debian/watch:
  + Don't hit on the -py3 release by blocking '-' from the version.
  + Mangle upstream version, inserting a tilde for beta/rc.

[ Stéphane Graber <stgraber@ubuntu.com> ]
 * Convert from python-support to dh_python2 (#654375)
  - debian/pyversions: Removed (no longer needed)
  - debian/rules
   + Replace call to dh_pysupport by dh_python2
   + Add --with=python2 to all dh calls
  - debian/control
   + Drop build-depends on python-support
   + Bump build-depends on python-all to >= 2.6.6-3~
   + Replace XS-Python-Version by X-Python-Version
   + Remove XB-Python-Version from binary package

[ Gustavo Noronha ]
* debian/control, debian/rules, debian/manpages:
 - use help2man to generate a manpage for cherryd at build time, since
  one is no longer shipped along with the source code
* debian/control:
- add python-nose to Build-Depends, since it's used during the
  documentation build for cross-reference generation

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Generic configuration system using unrepr.
 
2
 
 
3
Configuration data may be supplied as a Python dictionary, as a filename,
 
4
or as an open file object. When you supply a filename or file, Python's
 
5
builtin ConfigParser is used (with some extensions).
 
6
 
 
7
Namespaces
 
8
----------
 
9
 
 
10
Configuration keys are separated into namespaces by the first "." in the key.
 
11
 
 
12
The only key that cannot exist in a namespace is the "environment" entry.
 
13
This special entry 'imports' other config entries from a template stored in
 
14
the Config.environments dict.
 
15
 
 
16
You can define your own namespaces to be called when new config is merged
 
17
by adding a named handler to Config.namespaces. The name can be any string,
 
18
and the handler must be either a callable or a context manager.
 
19
"""
 
20
 
 
21
try:
 
22
    # Python 3.0+
 
23
    from configparser import ConfigParser
 
24
except ImportError:
 
25
    from ConfigParser import ConfigParser
 
26
 
 
27
try:
 
28
    set
 
29
except NameError:
 
30
    from sets import Set as set
 
31
 
 
32
try:
 
33
    basestring
 
34
except NameError:
 
35
    basestring = str
 
36
 
 
37
try:
 
38
    # Python 3
 
39
    import builtins
 
40
except ImportError:
 
41
    # Python 2
 
42
    import __builtin__ as builtins
 
43
 
 
44
import operator as _operator
 
45
import sys
 
46
 
 
47
def as_dict(config):
 
48
    """Return a dict from 'config' whether it is a dict, file, or filename."""
 
49
    if isinstance(config, basestring):
 
50
        config = Parser().dict_from_file(config)
 
51
    elif hasattr(config, 'read'):
 
52
        config = Parser().dict_from_file(config)
 
53
    return config
 
54
 
 
55
 
 
56
class NamespaceSet(dict):
 
57
    """A dict of config namespace names and handlers.
 
58
    
 
59
    Each config entry should begin with a namespace name; the corresponding
 
60
    namespace handler will be called once for each config entry in that
 
61
    namespace, and will be passed two arguments: the config key (with the
 
62
    namespace removed) and the config value.
 
63
    
 
64
    Namespace handlers may be any Python callable; they may also be
 
65
    Python 2.5-style 'context managers', in which case their __enter__
 
66
    method should return a callable to be used as the handler.
 
67
    See cherrypy.tools (the Toolbox class) for an example.
 
68
    """
 
69
    
 
70
    def __call__(self, config):
 
71
        """Iterate through config and pass it to each namespace handler.
 
72
        
 
73
        config
 
74
            A flat dict, where keys use dots to separate
 
75
            namespaces, and values are arbitrary.
 
76
        
 
77
        The first name in each config key is used to look up the corresponding
 
78
        namespace handler. For example, a config entry of {'tools.gzip.on': v}
 
79
        will call the 'tools' namespace handler with the args: ('gzip.on', v)
 
80
        """
 
81
        # Separate the given config into namespaces
 
82
        ns_confs = {}
 
83
        for k in config:
 
84
            if "." in k:
 
85
                ns, name = k.split(".", 1)
 
86
                bucket = ns_confs.setdefault(ns, {})
 
87
                bucket[name] = config[k]
 
88
        
 
89
        # I chose __enter__ and __exit__ so someday this could be
 
90
        # rewritten using Python 2.5's 'with' statement:
 
91
        # for ns, handler in self.iteritems():
 
92
        #     with handler as callable:
 
93
        #         for k, v in ns_confs.get(ns, {}).iteritems():
 
94
        #             callable(k, v)
 
95
        for ns, handler in self.items():
 
96
            exit = getattr(handler, "__exit__", None)
 
97
            if exit:
 
98
                callable = handler.__enter__()
 
99
                no_exc = True
 
100
                try:
 
101
                    try:
 
102
                        for k, v in ns_confs.get(ns, {}).items():
 
103
                            callable(k, v)
 
104
                    except:
 
105
                        # The exceptional case is handled here
 
106
                        no_exc = False
 
107
                        if exit is None:
 
108
                            raise
 
109
                        if not exit(*sys.exc_info()):
 
110
                            raise
 
111
                        # The exception is swallowed if exit() returns true
 
112
                finally:
 
113
                    # The normal and non-local-goto cases are handled here
 
114
                    if no_exc and exit:
 
115
                        exit(None, None, None)
 
116
            else:
 
117
                for k, v in ns_confs.get(ns, {}).items():
 
118
                    handler(k, v)
 
119
    
 
120
    def __repr__(self):
 
121
        return "%s.%s(%s)" % (self.__module__, self.__class__.__name__,
 
122
                              dict.__repr__(self))
 
123
    
 
124
    def __copy__(self):
 
125
        newobj = self.__class__()
 
126
        newobj.update(self)
 
127
        return newobj
 
128
    copy = __copy__
 
129
 
 
130
 
 
131
class Config(dict):
 
132
    """A dict-like set of configuration data, with defaults and namespaces.
 
133
    
 
134
    May take a file, filename, or dict.
 
135
    """
 
136
    
 
137
    defaults = {}
 
138
    environments = {}
 
139
    namespaces = NamespaceSet()
 
140
    
 
141
    def __init__(self, file=None, **kwargs):
 
142
        self.reset()
 
143
        if file is not None:
 
144
            self.update(file)
 
145
        if kwargs:
 
146
            self.update(kwargs)
 
147
    
 
148
    def reset(self):
 
149
        """Reset self to default values."""
 
150
        self.clear()
 
151
        dict.update(self, self.defaults)
 
152
    
 
153
    def update(self, config):
 
154
        """Update self from a dict, file or filename."""
 
155
        if isinstance(config, basestring):
 
156
            # Filename
 
157
            config = Parser().dict_from_file(config)
 
158
        elif hasattr(config, 'read'):
 
159
            # Open file object
 
160
            config = Parser().dict_from_file(config)
 
161
        else:
 
162
            config = config.copy()
 
163
        self._apply(config)
 
164
    
 
165
    def _apply(self, config):
 
166
        """Update self from a dict."""
 
167
        which_env = config.get('environment')
 
168
        if which_env:
 
169
            env = self.environments[which_env]
 
170
            for k in env:
 
171
                if k not in config:
 
172
                    config[k] = env[k]
 
173
        
 
174
        dict.update(self, config)
 
175
        self.namespaces(config)
 
176
    
 
177
    def __setitem__(self, k, v):
 
178
        dict.__setitem__(self, k, v)
 
179
        self.namespaces({k: v})
 
180
 
 
181
 
 
182
class Parser(ConfigParser):
 
183
    """Sub-class of ConfigParser that keeps the case of options and that 
 
184
    raises an exception if the file cannot be read.
 
185
    """
 
186
    
 
187
    def optionxform(self, optionstr):
 
188
        return optionstr
 
189
    
 
190
    def read(self, filenames):
 
191
        if isinstance(filenames, basestring):
 
192
            filenames = [filenames]
 
193
        for filename in filenames:
 
194
            # try:
 
195
            #     fp = open(filename)
 
196
            # except IOError:
 
197
            #     continue
 
198
            fp = open(filename)
 
199
            try:
 
200
                self._read(fp, filename)
 
201
            finally:
 
202
                fp.close()
 
203
    
 
204
    def as_dict(self, raw=False, vars=None):
 
205
        """Convert an INI file to a dictionary"""
 
206
        # Load INI file into a dict
 
207
        result = {}
 
208
        for section in self.sections():
 
209
            if section not in result:
 
210
                result[section] = {}
 
211
            for option in self.options(section):
 
212
                value = self.get(section, option, raw=raw, vars=vars)
 
213
                try:
 
214
                    value = unrepr(value)
 
215
                except Exception:
 
216
                    x = sys.exc_info()[1]
 
217
                    msg = ("Config error in section: %r, option: %r, "
 
218
                           "value: %r. Config values must be valid Python." %
 
219
                           (section, option, value))
 
220
                    raise ValueError(msg, x.__class__.__name__, x.args)
 
221
                result[section][option] = value
 
222
        return result
 
223
    
 
224
    def dict_from_file(self, file):
 
225
        if hasattr(file, 'read'):
 
226
            self.readfp(file)
 
227
        else:
 
228
            self.read(file)
 
229
        return self.as_dict()
 
230
 
 
231
 
 
232
# public domain "unrepr" implementation, found on the web and then improved.
 
233
 
 
234
 
 
235
class _Builder2:
 
236
    
 
237
    def build(self, o):
 
238
        m = getattr(self, 'build_' + o.__class__.__name__, None)
 
239
        if m is None:
 
240
            raise TypeError("unrepr does not recognize %s" %
 
241
                            repr(o.__class__.__name__))
 
242
        return m(o)
 
243
    
 
244
    def astnode(self, s):
 
245
        """Return a Python2 ast Node compiled from a string."""
 
246
        try:
 
247
            import compiler
 
248
        except ImportError:
 
249
            # Fallback to eval when compiler package is not available,
 
250
            # e.g. IronPython 1.0.
 
251
            return eval(s)
 
252
        
 
253
        p = compiler.parse("__tempvalue__ = " + s)
 
254
        return p.getChildren()[1].getChildren()[0].getChildren()[1]
 
255
    
 
256
    def build_Subscript(self, o):
 
257
        expr, flags, subs = o.getChildren()
 
258
        expr = self.build(expr)
 
259
        subs = self.build(subs)
 
260
        return expr[subs]
 
261
    
 
262
    def build_CallFunc(self, o):
 
263
        children = map(self.build, o.getChildren())
 
264
        callee = children.pop(0)
 
265
        kwargs = children.pop() or {}
 
266
        starargs = children.pop() or ()
 
267
        args = tuple(children) + tuple(starargs)
 
268
        return callee(*args, **kwargs)
 
269
    
 
270
    def build_List(self, o):
 
271
        return map(self.build, o.getChildren())
 
272
    
 
273
    def build_Const(self, o):
 
274
        return o.value
 
275
    
 
276
    def build_Dict(self, o):
 
277
        d = {}
 
278
        i = iter(map(self.build, o.getChildren()))
 
279
        for el in i:
 
280
            d[el] = i.next()
 
281
        return d
 
282
    
 
283
    def build_Tuple(self, o):
 
284
        return tuple(self.build_List(o))
 
285
    
 
286
    def build_Name(self, o):
 
287
        name = o.name
 
288
        if name == 'None':
 
289
            return None
 
290
        if name == 'True':
 
291
            return True
 
292
        if name == 'False':
 
293
            return False
 
294
        
 
295
        # See if the Name is a package or module. If it is, import it.
 
296
        try:
 
297
            return modules(name)
 
298
        except ImportError:
 
299
            pass
 
300
        
 
301
        # See if the Name is in builtins.
 
302
        try:
 
303
            return getattr(builtins, name)
 
304
        except AttributeError:
 
305
            pass
 
306
        
 
307
        raise TypeError("unrepr could not resolve the name %s" % repr(name))
 
308
    
 
309
    def build_Add(self, o):
 
310
        left, right = map(self.build, o.getChildren())
 
311
        return left + right
 
312
 
 
313
    def build_Mul(self, o):
 
314
        left, right = map(self.build, o.getChildren())
 
315
        return left * right
 
316
    
 
317
    def build_Getattr(self, o):
 
318
        parent = self.build(o.expr)
 
319
        return getattr(parent, o.attrname)
 
320
    
 
321
    def build_NoneType(self, o):
 
322
        return None
 
323
    
 
324
    def build_UnarySub(self, o):
 
325
        return -self.build(o.getChildren()[0])
 
326
    
 
327
    def build_UnaryAdd(self, o):
 
328
        return self.build(o.getChildren()[0])
 
329
 
 
330
 
 
331
class _Builder3:
 
332
    
 
333
    def build(self, o):
 
334
        m = getattr(self, 'build_' + o.__class__.__name__, None)
 
335
        if m is None:
 
336
            raise TypeError("unrepr does not recognize %s" %
 
337
                            repr(o.__class__.__name__))
 
338
        return m(o)
 
339
    
 
340
    def astnode(self, s):
 
341
        """Return a Python3 ast Node compiled from a string."""
 
342
        try:
 
343
            import ast
 
344
        except ImportError:
 
345
            # Fallback to eval when ast package is not available,
 
346
            # e.g. IronPython 1.0.
 
347
            return eval(s)
 
348
 
 
349
        p = ast.parse("__tempvalue__ = " + s)
 
350
        return p.body[0].value
 
351
 
 
352
    def build_Subscript(self, o):
 
353
        return self.build(o.value)[self.build(o.slice)]
 
354
    
 
355
    def build_Index(self, o):
 
356
        return self.build(o.value)
 
357
    
 
358
    def build_Call(self, o):
 
359
        callee = self.build(o.func)
 
360
        
 
361
        if o.args is None:
 
362
            args = ()
 
363
        else: 
 
364
            args = tuple([self.build(a) for a in o.args]) 
 
365
        
 
366
        if o.starargs is None:
 
367
            starargs = ()
 
368
        else:
 
369
            starargs = self.build(o.starargs)
 
370
        
 
371
        if o.kwargs is None:
 
372
            kwargs = {}
 
373
        else:
 
374
            kwargs = self.build(o.kwargs)
 
375
        
 
376
        return callee(*(args + starargs), **kwargs)
 
377
    
 
378
    def build_List(self, o):
 
379
        return list(map(self.build, o.elts))
 
380
    
 
381
    def build_Str(self, o):
 
382
        return o.s
 
383
    
 
384
    def build_Num(self, o):
 
385
        return o.n
 
386
    
 
387
    def build_Dict(self, o):
 
388
        return dict([(self.build(k), self.build(v))
 
389
                     for k, v in zip(o.keys, o.values)])
 
390
    
 
391
    def build_Tuple(self, o):
 
392
        return tuple(self.build_List(o))
 
393
    
 
394
    def build_Name(self, o):
 
395
        name = o.id
 
396
        if name == 'None':
 
397
            return None
 
398
        if name == 'True':
 
399
            return True
 
400
        if name == 'False':
 
401
            return False
 
402
        
 
403
        # See if the Name is a package or module. If it is, import it.
 
404
        try:
 
405
            return modules(name)
 
406
        except ImportError:
 
407
            pass
 
408
        
 
409
        # See if the Name is in builtins.
 
410
        try:
 
411
            import builtins
 
412
            return getattr(builtins, name)
 
413
        except AttributeError:
 
414
            pass
 
415
        
 
416
        raise TypeError("unrepr could not resolve the name %s" % repr(name))
 
417
        
 
418
    def build_UnaryOp(self, o):
 
419
        op, operand = map(self.build, [o.op, o.operand])
 
420
        return op(operand)
 
421
    
 
422
    def build_BinOp(self, o):
 
423
        left, op, right = map(self.build, [o.left, o.op, o.right]) 
 
424
        return op(left, right)
 
425
 
 
426
    def build_Add(self, o):
 
427
        return _operator.add
 
428
 
 
429
    def build_Mult(self, o):
 
430
        return _operator.mul
 
431
        
 
432
    def build_USub(self, o):
 
433
        return _operator.neg
 
434
 
 
435
    def build_Attribute(self, o):
 
436
        parent = self.build(o.value)
 
437
        return getattr(parent, o.attr)
 
438
 
 
439
    def build_NoneType(self, o):
 
440
        return None
 
441
 
 
442
 
 
443
def unrepr(s):
 
444
    """Return a Python object compiled from a string."""
 
445
    if not s:
 
446
        return s
 
447
    if sys.version_info < (3, 0):
 
448
        b = _Builder2()
 
449
    else:
 
450
        b = _Builder3()
 
451
    obj = b.astnode(s)
 
452
    return b.build(obj)
 
453
 
 
454
 
 
455
def modules(modulePath):
 
456
    """Load a module and retrieve a reference to that module."""
 
457
    try:
 
458
        mod = sys.modules[modulePath]
 
459
        if mod is None:
 
460
            raise KeyError()
 
461
    except KeyError:
 
462
        # The last [''] is important.
 
463
        mod = __import__(modulePath, globals(), locals(), [''])
 
464
    return mod
 
465
 
 
466
def attributes(full_attribute_name):
 
467
    """Load a module and retrieve an attribute of that module."""
 
468
    
 
469
    # Parse out the path, module, and attribute
 
470
    last_dot = full_attribute_name.rfind(".")
 
471
    attr_name = full_attribute_name[last_dot + 1:]
 
472
    mod_path = full_attribute_name[:last_dot]
 
473
    
 
474
    mod = modules(mod_path)
 
475
    # Let an AttributeError propagate outward.
 
476
    try:
 
477
        attr = getattr(mod, attr_name)
 
478
    except AttributeError:
 
479
        raise AttributeError("'%s' object has no attribute '%s'"
 
480
                             % (mod_path, attr_name))
 
481
    
 
482
    # Return a reference to the attribute.
 
483
    return attr
 
484
 
 
485