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 | |
29 | import sys as SYS | |
30 | import types as TY | |
31 | ||
32 | from auto import PACKAGE, VERSION | |
33 | ||
34 | ### Configuration is done by interpreting a file of Python code. We expect | |
35 | ### the code to define a number of variables in its global scope. We import | |
36 | ### a number of identifiers (named in the `EXPORT' list) to this module, and | |
37 | ### also our entire parent module as `chpwd'. | |
38 | ||
39 | ## Names which ought to be exported to the configuration module. | |
40 | _EXPORT = {} | |
41 | ||
42 | ## The configuration module. | |
43 | CFG = TY.ModuleType('chpwd_config') | |
44 | ||
45 | ## A list of hooks to call once configuration is complete. | |
46 | _HOOKS = [] | |
47 | def hook(func): | |
48 | """Decorator for post-configuration hooks.""" | |
49 | _HOOKS.append(func) | |
50 | return func | |
51 | ||
52 | ## A suitable set of defaults. | |
53 | DEFAULTS = {} | |
54 | ||
55 | def export(*names, **kw): | |
56 | """ | |
57 | Export the names to the configuration module from the caller's environment. | |
58 | """ | |
59 | ||
60 | ## Find the caller's global environment. Please don't try this at home. | |
61 | try: raise Exception | |
62 | except: tb = SYS.exc_info()[2] | |
63 | env = tb.tb_frame.f_back.f_globals | |
64 | ||
65 | ## Export things. | |
66 | for name in names: | |
67 | _EXPORT[name] = env[name] | |
68 | _EXPORT.update(kw) | |
69 | ||
70 | ## Some things to export for sure. | |
71 | export('PACKAGE', 'VERSION', 'ENV', CONF = SYS.modules[__name__]) | |
72 | ||
73 | def loadconfig(config): | |
74 | """ | |
75 | Load the configuration, populating the `CFG' module with settings. | |
76 | """ | |
77 | ||
78 | ## Make a new module for the configuration, and import ourselves into it. | |
79 | d = CFG.__dict__ | |
80 | d.update(_EXPORT) | |
81 | d.update(DEFAULTS) | |
82 | ||
83 | ## And run the configuration code. | |
84 | with open(config) as f: | |
85 | exec f in d | |
86 | ||
87 | ## Run the hooks. | |
88 | for func in _HOOKS: | |
89 | func() | |
90 | ||
91 | ###----- That's all, folks -------------------------------------------------- |