Commit | Line | Data |
---|---|---|
a2916c06 MW |
1 | ### -*-python-*- |
2 | ### | |
3 | ### Configuration handling | |
4 | ### | |
5 | ### (c) 2013 Mark Wooding | |
6 | ### | |
7 | ||
8 | ###----- Licensing notice --------------------------------------------------- | |
9 | ### | |
10 | ### This file is part of Chopwood: a password-changing service. | |
11 | ### | |
12 | ### Chopwood is free software; you can redistribute it and/or modify | |
13 | ### it under the terms of the GNU Affero General Public License as | |
14 | ### published by the Free Software Foundation; either version 3 of the | |
15 | ### License, or (at your option) any later version. | |
16 | ### | |
17 | ### Chopwood is distributed in the hope that it will be useful, | |
18 | ### but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ### GNU Affero General Public License for more details. | |
21 | ### | |
22 | ### You should have received a copy of the GNU Affero General Public | |
23 | ### License along with Chopwood; if not, see | |
24 | ### <http://www.gnu.org/licenses/>. | |
25 | ||
26 | from __future__ import with_statement | |
27 | ||
28 | import os as OS; ENV = OS.environ | |
9fc9351d | 29 | import output as O; OUT = O.OUT |
a2916c06 MW |
30 | import sys as SYS |
31 | import types as TY | |
32 | ||
33 | from auto import PACKAGE, VERSION | |
34 | ||
35 | ### Configuration is done by interpreting a file of Python code. We expect | |
36 | ### the code to define a number of variables in its global scope. We import | |
37 | ### a number of identifiers (named in the `EXPORT' list) to this module, and | |
38 | ### also our entire parent module as `chpwd'. | |
39 | ||
40 | ## Names which ought to be exported to the configuration module. | |
41 | _EXPORT = {} | |
42 | ||
43 | ## The configuration module. | |
44 | CFG = TY.ModuleType('chpwd_config') | |
45 | ||
46 | ## A list of hooks to call once configuration is complete. | |
47 | _HOOKS = [] | |
48 | def hook(func): | |
49 | """Decorator for post-configuration hooks.""" | |
50 | _HOOKS.append(func) | |
51 | return func | |
52 | ||
53 | ## A suitable set of defaults. | |
54 | DEFAULTS = {} | |
55 | ||
56 | def export(*names, **kw): | |
57 | """ | |
58 | Export the names to the configuration module from the caller's environment. | |
59 | """ | |
60 | ||
61 | ## Find the caller's global environment. Please don't try this at home. | |
62 | try: raise Exception | |
63 | except: tb = SYS.exc_info()[2] | |
64 | env = tb.tb_frame.f_back.f_globals | |
65 | ||
66 | ## Export things. | |
67 | for name in names: | |
68 | _EXPORT[name] = env[name] | |
69 | _EXPORT.update(kw) | |
70 | ||
71 | ## Some things to export for sure. | |
72 | export('PACKAGE', 'VERSION', 'ENV', CONF = SYS.modules[__name__]) | |
73 | ||
74 | def loadconfig(config): | |
75 | """ | |
76 | Load the configuration, populating the `CFG' module with settings. | |
77 | """ | |
78 | ||
79 | ## Make a new module for the configuration, and import ourselves into it. | |
80 | d = CFG.__dict__ | |
81 | d.update(_EXPORT) | |
82 | d.update(DEFAULTS) | |
83 | ||
84 | ## And run the configuration code. | |
9fc9351d MW |
85 | try: |
86 | with open(config) as f: | |
87 | exec f in d | |
88 | except IOError, e: | |
89 | OUT.warn("couldn't open configuration file `%s': %s" % | |
90 | (config, e.strerror)) | |
a2916c06 MW |
91 | |
92 | ## Run the hooks. | |
93 | for func in _HOOKS: | |
94 | func() | |
95 | ||
96 | ###----- That's all, folks -------------------------------------------------- |