-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathyfigure.py
149 lines (109 loc) · 4.8 KB
/
yfigure.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import os
from os.path import expanduser
import sys
import yaml
"""
A SIMPLE BUT EFFECTIVE CONFIGURATION CLASS FOR PYTHON
# import YAMLConfig from wherever it is..
from .yfigure import YAMLConfig
# subclass YAMLConfig to make config class for your application, list config_files in the order you want them tried.
class MyConfig(YAMLConfig):
config_files = (
'~/myapp.conf',
'/etc/myapp.conf'
)
# THERE ARE A FEW TRICKS FOR READING THE CONF AND SELECTING DIFFERENT CONFIGURATIONS FROM THE SAME FILE.
# First, by default, if you do a simple:
config = MyConfig()
# ..and you put your configuration values in the file.. then they will be read and the top level yaml elements will
# become attributes of config, so you can do:
# IN YAML:
hellotext: hello
goodbyetext: goodbye
# then get the value of hellotext:
print(config.hellotext)
# but you can also have multiple configurations within the same file:
# IN YAML:
english:
hellotext: hello
goodbyetext: goodbye
spanish:
hellotext: hola
goodbyetext: adios
# Then you can select them one of two ways, either from your code or from within yaml.
# Within YAML, there's a special config top level variable called "configuration":
configuration: spanish
# Now everything under spanish becomes the top level of the config and everything else in the file is ignored.
# You can also select it from within your code by passing it to the constructor of the config class:
config = MyConfig(configuration='spanish')
# You can force the base (non-nested) configuration by passing configuration='root' anywhere you can specify a configuration name.
# If you wanted to control it from an environment variable you could:
import os # import os if you haven't already...
config = MyConfig(configuration=os.environ['MYAPP_CONFIG'])
# You have a choice there.. the way I read the environment variable, it'll raise an exception if MYAPP_CONFIG isn't set.
# but let's say you do:
config = MyConfig(configuration=os.environ.get('MYAPP_CONFIG'))
# in that case if MYAPP_CONFIG is not set, it'll pass configuration=None, which defaults to root.
# If the value of configuration (other than None or 'root') is not present in the top level of the YAML, ValueError is raised.
# Anything YAML supports is ok, any level of nesting, but only the first level is put in attributes, so if you do, in YAML:
db:
name: thegoods
servers:
- godfather1
- godfather2
# You would access the top level as a direct member of config but everything else is just as you would expect:
config.db['name']
config.db['servers'][0]
# There's no easy way to override actual config values from the environment, only which section of the config file is used.
# But it would be easy enough to add.
"""
def open_config(pathnames, verbose=False):
def log(s):
if verbose:
sys.stderr.write(s)
pathname = stream = None
for option in pathnames:
if pathname:
log(',')
pathname = expanduser(option)
log(' %s' % pathname)
if not os.path.isfile(pathname):
log(" NO")
continue
try:
stream = open(pathname)
log(" YES")
break
except Exception as e:
log("\n%s: %s.\n" % (pathname, e))
raise
log(".\n")
if stream:
return stream
raise FileNotFoundError('None of: %s were found.' % str(pathnames))
class YAMLConfig(object):
def __init__(self, config_files=None, configuration=None, verbose=False):
config_files = getattr(self, 'config_files', config_files)
error_tpl = type(self).__name__ + ' Error loading file: %s.'
if not config_files:
raise NotImplementedError(
'YAMLConfig requires "config_files" as either a member of a subclass, or a kwarg of __init__. It was neither.'
)
try:
with open_config(config_files) as stream:
self._data = data = yaml.load(stream, Loader=yaml.FullLoader)
if configuration == '_default_':
configuration = None
self.configuration = configuration or data.get('configuration', 'root')
if isinstance(data, dict):
if not self.configuration == 'root':
if not self.configuration in data:
raise ValueError(error_tpl % ('configuration named "%s" not found in YAML.' % self.configuration))
data = data[self.configuration]
for a, v in data.items():
setattr(self, a, v)
except Exception as e:
sys.stderr.write((error_tpl % e) + '\n')
if isinstance(e, FileNotFoundError):
raise
raise ValueError(error_tpl % e)