##// END OF EJS Templates
ui: introduce new config parser
Matt Mackall -
r8144:fca54469 default
parent child Browse files
Show More
@@ -0,0 +1,93 b''
1 from i18n import _
2 import re, error
3
4 class sortdict(dict):
5 'a simple append-only sorted dictionary'
6 def __init__(self, data=None):
7 self._list = []
8 if data:
9 if hasattr(data, '_list'):
10 self._list = list(data._list)
11 self.update(data)
12 def copy(self):
13 return sortdict(self)
14 def __setitem__(self, key, val):
15 if key in self:
16 self._list.remove(key)
17 self._list.append(key)
18 dict.__setitem__(self, key, val)
19 def __iter__(self):
20 return self._list.__iter__()
21 def update(self, src):
22 for k in src:
23 self[k] = src[k]
24 def items(self):
25 return [(k,self[k]) for k in self._list]
26
27 class config:
28 def __init__(self, data=None):
29 self._data = {}
30 if data:
31 for k in data._data:
32 self._data[k] = data[k].copy()
33 def copy(self):
34 return config(self)
35 def __contains__(self, section):
36 return section in self._data
37 def update(self, src, sections=None):
38 if not sections:
39 sections = src.sections()
40 for s in sections:
41 if s not in src:
42 continue
43 if s not in self:
44 self._data[s] = sortdict()
45 for k in src._data[s]:
46 self._data[s][k] = src._data[s][k]
47 def get(self, section, item, default=None):
48 return self._data.get(section, {}).get(item, (default, ""))[0]
49 def getsource(self, section, item):
50 return self._data.get(section, {}).get(item, (None, ""))[1]
51 def sections(self):
52 return sorted(self._data.keys())
53 def items(self, section):
54 return [(k, v[0]) for k,v in self._data.get(section, {}).items()]
55 def set(self, section, item, value, source=""):
56 if section not in self:
57 self._data[section] = sortdict()
58 self._data[section][item] = (value, source)
59
60 def read(self, path, fp):
61 sectionre = re.compile(r'\[([^\[]+)\]')
62 itemre = re.compile(r'([^=\s]+)\s*=\s*(.*)')
63 contre = re.compile(r'\s+(\S.*)')
64 emptyre = re.compile(r'(;|#|\s*$)')
65 section = ""
66 item = None
67 line = 0
68 cont = 0
69 for l in fp:
70 line += 1
71 if cont:
72 m = contre.match(l)
73 if m:
74 v = self.get(section, item) + "\n" + m.group(1)
75 self.set(section, item, v, "%s:%d" % (path, line))
76 continue
77 item = None
78 if emptyre.match(l):
79 continue
80 m = sectionre.match(l)
81 if m:
82 section = m.group(1)
83 if section not in self:
84 self._data[section] = sortdict()
85 continue
86 m = itemre.match(l)
87 if m:
88 item = m.group(1)
89 self.set(section, item, m.group(2), "%s:%d" % (path, line))
90 cont = 1
91 continue
92 raise error.ConfigError(_('config error at %s:%d: \'%s\'')
93 % (path, line, l.rstrip()))
@@ -55,6 +55,8 b' def _runcatch(ui, args):'
55 55 except error.AmbiguousCommand, inst:
56 56 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
57 57 (inst.args[0], " ".join(inst.args[1])))
58 except error.ConfigError, inst:
59 ui.warn(_("hg: %s\n") % inst.args[0])
58 60 except error.LockHeld, inst:
59 61 if inst.errno == errno.ETIMEDOUT:
60 62 reason = _('timed out waiting for lock held by %s') % inst.locker
@@ -28,6 +28,9 b' class LookupError(RevlogError, KeyError)'
28 28 class ParseError(Exception):
29 29 """Exception raised on errors in parsing the command line."""
30 30
31 class ConfigError(Exception):
32 'Exception raised when parsing config files'
33
31 34 class RepoError(Exception):
32 35 pass
33 36
@@ -7,27 +7,19 b''
7 7
8 8 from i18n import _
9 9 import errno, getpass, os, re, socket, sys, tempfile
10 import ConfigParser, traceback, util
10 import config, traceback, util, error
11 11
12 def updateconfig(source, dest, sections=None):
13 if not sections:
14 sections = source.sections()
15 for section in sections:
16 if not dest.has_section(section):
17 dest.add_section(section)
18 if not source.has_section(section):
19 continue
20 for name, value in source.items(section, raw=True):
21 dest.set(section, name, value)
12 _booleans = {'1':True, 'yes':True, 'true':True, 'on':True,
13 '0':False, 'no':False, 'false':False, 'off':False}
22 14
23 15 class ui(object):
24 16 def __init__(self, parentui=None):
25 17 self.buffers = []
26 18 self.quiet = self.verbose = self.debugflag = self.traceback = False
27 19 self.interactive = self.report_untrusted = True
28 self.overlay = util.configparser()
29 self.cdata = util.configparser()
30 self.ucdata = util.configparser()
20 self.overlay = config.config()
21 self.cdata = config.config()
22 self.ucdata = config.config()
31 23 self.parentui = None
32 24 self.trusted_users = {}
33 25 self.trusted_groups = {}
@@ -35,10 +27,10 b' class ui(object):'
35 27 if parentui:
36 28 # parentui may point to an ui object which is already a child
37 29 self.parentui = parentui.parentui or parentui
38 updateconfig(self.parentui.cdata, self.cdata)
39 updateconfig(self.parentui.ucdata, self.ucdata)
30 self.cdata.update(self.parentui.cdata)
31 self.ucdata.update(self.parentui.ucdata)
40 32 # we want the overlay from the parent, not the root
41 updateconfig(parentui.overlay, self.overlay)
33 self.overlay.update(parentui.overlay)
42 34 self.buffers = parentui.buffers
43 35 self.trusted_users = parentui.trusted_users.copy()
44 36 self.trusted_groups = parentui.trusted_groups.copy()
@@ -89,22 +81,21 b' class ui(object):'
89 81 return
90 82 raise
91 83
92 cdata = util.configparser()
84 cdata = config.config()
93 85 trusted = sections or assumetrusted or self._is_trusted(fp, filename)
94 86
95 87 try:
96 cdata.readfp(fp, filename)
97 except ConfigParser.ParsingError, inst:
98 msg = _("Failed to parse %s\n%s") % (filename, inst)
88 cdata.read(filename, fp)
89 except error.ConfigError, inst:
99 90 if trusted:
100 raise util.Abort(msg)
101 self.warn(_("Ignored: %s\n") % msg)
91 raise
92 self.warn(_("Ignored: %s\n") % str(inst))
102 93
103 94 if trusted:
104 updateconfig(cdata, self.cdata, sections)
105 updateconfig(self.overlay, self.cdata, sections)
106 updateconfig(cdata, self.ucdata, sections)
107 updateconfig(self.overlay, self.ucdata, sections)
95 self.cdata.update(cdata, sections)
96 self.cdata.update(self.overlay, sections)
97 self.ucdata.update(cdata, sections)
98 self.ucdata.update(self.overlay, sections)
108 99
109 100 if root is None:
110 101 root = os.path.expanduser('~')
@@ -117,7 +108,7 b' class ui(object):'
117 108 root = os.getcwd()
118 109 items = section and [(name, value)] or []
119 110 for cdata in self.cdata, self.ucdata, self.overlay:
120 if not items and cdata.has_section('paths'):
111 if not items and 'paths' in cdata:
121 112 pathsitems = cdata.items('paths')
122 113 else:
123 114 pathsitems = items
@@ -149,8 +140,6 b' class ui(object):'
149 140
150 141 def setconfig(self, section, name, value):
151 142 for cdata in (self.overlay, self.cdata, self.ucdata):
152 if not cdata.has_section(section):
153 cdata.add_section(section)
154 143 cdata.set(section, name, value)
155 144 self.fixconfig(section, name, value)
156 145
@@ -159,37 +148,23 b' class ui(object):'
159 148 return self.ucdata
160 149 return self.cdata
161 150
162 def _config(self, section, name, default, funcname, untrusted, abort):
163 cdata = self._get_cdata(untrusted)
164 if cdata.has_option(section, name):
165 try:
166 func = getattr(cdata, funcname)
167 return func(section, name)
168 except (ConfigParser.InterpolationError, ValueError), inst:
169 msg = _("Error in configuration section [%s] "
170 "parameter '%s':\n%s") % (section, name, inst)
171 if abort:
172 raise util.Abort(msg)
173 self.warn(_("Ignored: %s\n") % msg)
174 return default
175
176 def _configcommon(self, section, name, default, funcname, untrusted):
177 value = self._config(section, name, default, funcname,
178 untrusted, abort=True)
151 def config(self, section, name, default=None, untrusted=False):
152 value = self._get_cdata(untrusted).get(section, name, default)
179 153 if self.debugflag and not untrusted:
180 uvalue = self._config(section, name, None, funcname,
181 untrusted=True, abort=False)
154 uvalue = self.ucdata.get(section, name)
182 155 if uvalue is not None and uvalue != value:
183 156 self.warn(_("Ignoring untrusted configuration option "
184 157 "%s.%s = %s\n") % (section, name, uvalue))
185 158 return value
186 159
187 def config(self, section, name, default=None, untrusted=False):
188 return self._configcommon(section, name, default, 'get', untrusted)
189
190 160 def configbool(self, section, name, default=False, untrusted=False):
191 return self._configcommon(section, name, default, 'getboolean',
192 untrusted)
161 v = self.config(section, name, None, untrusted)
162 if v == None:
163 return default
164 if v.lower() not in _booleans:
165 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
166 % (section, name, v))
167 return _booleans[v.lower()]
193 168
194 169 def configlist(self, section, name, default=None, untrusted=False):
195 170 """Return a list of comma/space separated strings"""
@@ -202,38 +177,20 b' class ui(object):'
202 177
203 178 def has_section(self, section, untrusted=False):
204 179 '''tell whether section exists in config.'''
205 cdata = self._get_cdata(untrusted)
206 return cdata.has_section(section)
207
208 def _configitems(self, section, untrusted, abort):
209 items = {}
210 cdata = self._get_cdata(untrusted)
211 if cdata.has_section(section):
212 try:
213 items.update(dict(cdata.items(section)))
214 except ConfigParser.InterpolationError, inst:
215 msg = _("Error in configuration section [%s]:\n"
216 "%s") % (section, inst)
217 if abort:
218 raise util.Abort(msg)
219 self.warn(_("Ignored: %s\n") % msg)
220 return items
180 return section in self._get_cdata(untrusted)
221 181
222 182 def configitems(self, section, untrusted=False):
223 items = self._configitems(section, untrusted=untrusted, abort=True)
183 items = self._get_cdata(untrusted).items(section)
224 184 if self.debugflag and not untrusted:
225 uitems = self._configitems(section, untrusted=True, abort=False)
226 for k in util.sort(uitems):
227 if uitems[k] != items.get(k):
185 for k,v in self.ucdata.items(section):
186 if self.cdata.get(section, k) != v:
228 187 self.warn(_("Ignoring untrusted configuration option "
229 "%s.%s = %s\n") % (section, k, uitems[k]))
230 return util.sort(items.items())
188 "%s.%s = %s\n") % (section, k, v))
189 return items
231 190
232 191 def walkconfig(self, untrusted=False):
233 192 cdata = self._get_cdata(untrusted)
234 sections = cdata.sections()
235 sections.sort()
236 for section in sections:
193 for section in cdata.sections():
237 194 for name, value in self.configitems(section, untrusted):
238 195 yield section, name, str(value).replace('\n', '\\n')
239 196
@@ -1,16 +1,13 b''
1 abort: Failed to parse .../t/.hg/hgrc
2 File contains no section headers.
3 file: .../t/.hg/hgrc, line: 1
4 'invalid\n'
1 hg: config error at .../t/.hg/hgrc:1: 'invalid'
5 2 updating working directory
6 3 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
7 4 [paths]
8 5 default = .../foo%%bar
9 default = .../foo%bar
6 default = .../foo%%bar
10 7 bundle.mainreporoot=.../foobar
11 8 defaults.backout=-d "0 0"
12 9 defaults.commit=-d "0 0"
13 10 defaults.debugrawcommit=-d "0 0"
14 11 defaults.tag=-d "0 0"
15 paths.default=.../foo%bar
12 paths.default=.../foo%%bar
16 13 ui.slash=True
@@ -3,7 +3,7 b''
3 3 # monkey-patching some functions in the util module
4 4
5 5 import os
6 from mercurial import ui, util
6 from mercurial import ui, util, error
7 7
8 8 hgrc = os.environ['HGRCPATH']
9 9 f = open(hgrc)
@@ -85,7 +85,6 b" os.mkdir('.hg')"
85 85 f = open('.hg/hgrc', 'w')
86 86 f.write('[paths]\n')
87 87 f.write('local = /another/path\n\n')
88 f.write('interpolated = %(global)s%(local)s\n\n')
89 88 f.close()
90 89
91 90 #print '# Everything is run by user foo, group bar\n'
@@ -154,10 +153,8 b' util.username = username'
154 153 u2.readconfig('.hg/hgrc')
155 154 print 'trusted:'
156 155 print u2.config('foobar', 'baz')
157 print u2.config('paths', 'interpolated')
158 156 print 'untrusted:'
159 157 print u2.config('foobar', 'baz', untrusted=True)
160 print u2.config('paths', 'interpolated', untrusted=True)
161 158
162 159 print
163 160 print "# error handling"
@@ -179,33 +176,15 b" testui(user='abc', group='def', debug=Tr"
179 176 print
180 177 print "# parse error"
181 178 f = open('.hg/hgrc', 'w')
182 f.write('foo = bar')
183 f.close()
184 testui(user='abc', group='def', silent=True)
185 assertraises(lambda: testui(debug=True, silent=True))
186
187 print
188 print "# interpolation error"
189 f = open('.hg/hgrc', 'w')
190 f.write('[foo]\n')
191 f.write('bar = %(')
179 f.write('foo')
192 180 f.close()
193 u = testui(debug=True, silent=True)
194 print '# regular config:'
195 print ' trusted',
196 assertraises(lambda: u.config('foo', 'bar'))
197 print 'untrusted',
198 assertraises(lambda: u.config('foo', 'bar', untrusted=True))
199 181
200 u = testui(user='abc', group='def', debug=True, silent=True)
201 print ' trusted ',
202 print u.config('foo', 'bar')
203 print 'untrusted',
204 assertraises(lambda: u.config('foo', 'bar', untrusted=True))
182 try:
183 testui(user='abc', group='def', silent=True)
184 except error.ConfigError, inst:
185 print inst
205 186
206 print '# configitems:'
207 print ' trusted ',
208 print u.configitems('foo')
209 print 'untrusted',
210 assertraises(lambda: u.configitems('foo', untrusted=True))
211
187 try:
188 testui(debug=True, silent=True)
189 except error.ConfigError, inst:
190 print inst
@@ -1,21 +1,17 b''
1 1 # same user, same group
2 2 trusted
3 3 global = /some/path
4 interpolated = /some/path/another/path
5 4 local = /another/path
6 5 untrusted
7 6 . . global = /some/path
8 . . interpolated = /some/path/another/path
9 7 . . local = /another/path
10 8
11 9 # same user, different group
12 10 trusted
13 11 global = /some/path
14 interpolated = /some/path/another/path
15 12 local = /another/path
16 13 untrusted
17 14 . . global = /some/path
18 . . interpolated = /some/path/another/path
19 15 . . local = /another/path
20 16
21 17 # different user, same group
@@ -24,17 +20,14 b' trusted'
24 20 global = /some/path
25 21 untrusted
26 22 . . global = /some/path
27 . . interpolated = /some/path/another/path
28 23 . . local = /another/path
29 24
30 25 # different user, same group, but we trust the group
31 26 trusted
32 27 global = /some/path
33 interpolated = /some/path/another/path
34 28 local = /another/path
35 29 untrusted
36 30 . . global = /some/path
37 . . interpolated = /some/path/another/path
38 31 . . local = /another/path
39 32
40 33 # different user, different group
@@ -43,70 +36,57 b' trusted'
43 36 global = /some/path
44 37 untrusted
45 38 . . global = /some/path
46 . . interpolated = /some/path/another/path
47 39 . . local = /another/path
48 40
49 41 # different user, different group, but we trust the user
50 42 trusted
51 43 global = /some/path
52 interpolated = /some/path/another/path
53 44 local = /another/path
54 45 untrusted
55 46 . . global = /some/path
56 . . interpolated = /some/path/another/path
57 47 . . local = /another/path
58 48
59 49 # different user, different group, but we trust the group
60 50 trusted
61 51 global = /some/path
62 interpolated = /some/path/another/path
63 52 local = /another/path
64 53 untrusted
65 54 . . global = /some/path
66 . . interpolated = /some/path/another/path
67 55 . . local = /another/path
68 56
69 57 # different user, different group, but we trust the user and the group
70 58 trusted
71 59 global = /some/path
72 interpolated = /some/path/another/path
73 60 local = /another/path
74 61 untrusted
75 62 . . global = /some/path
76 . . interpolated = /some/path/another/path
77 63 . . local = /another/path
78 64
79 65 # we trust all users
80 66 # different user, different group
81 67 trusted
82 68 global = /some/path
83 interpolated = /some/path/another/path
84 69 local = /another/path
85 70 untrusted
86 71 . . global = /some/path
87 . . interpolated = /some/path/another/path
88 72 . . local = /another/path
89 73
90 74 # we trust all groups
91 75 # different user, different group
92 76 trusted
93 77 global = /some/path
94 interpolated = /some/path/another/path
95 78 local = /another/path
96 79 untrusted
97 80 . . global = /some/path
98 . . interpolated = /some/path/another/path
99 81 . . local = /another/path
100 82
101 83 # we trust all users and groups
102 84 # different user, different group
103 85 trusted
104 86 global = /some/path
105 interpolated = /some/path/another/path
106 87 local = /another/path
107 88 untrusted
108 89 . . global = /some/path
109 . . interpolated = /some/path/another/path
110 90 . . local = /another/path
111 91
112 92 # we don't get confused by users and groups with the same name
@@ -116,29 +96,24 b' trusted'
116 96 global = /some/path
117 97 untrusted
118 98 . . global = /some/path
119 . . interpolated = /some/path/another/path
120 99 . . local = /another/path
121 100
122 101 # list of user names
123 102 # different user, different group, but we trust the user
124 103 trusted
125 104 global = /some/path
126 interpolated = /some/path/another/path
127 105 local = /another/path
128 106 untrusted
129 107 . . global = /some/path
130 . . interpolated = /some/path/another/path
131 108 . . local = /another/path
132 109
133 110 # list of group names
134 111 # different user, different group, but we trust the group
135 112 trusted
136 113 global = /some/path
137 interpolated = /some/path/another/path
138 114 local = /another/path
139 115 untrusted
140 116 . . global = /some/path
141 . . interpolated = /some/path/another/path
142 117 . . local = /another/path
143 118
144 119 # Can't figure out the name of the user running this process
@@ -148,20 +123,16 b' trusted'
148 123 global = /some/path
149 124 untrusted
150 125 . . global = /some/path
151 . . interpolated = /some/path/another/path
152 126 . . local = /another/path
153 127
154 128 # prints debug warnings
155 129 # different user, different group
156 130 Not trusting file .hg/hgrc from untrusted user abc, group def
157 131 trusted
158 Ignoring untrusted configuration option paths.interpolated = /some/path/another/path
159 132 Ignoring untrusted configuration option paths.local = /another/path
160 133 global = /some/path
161 134 untrusted
162 135 . . global = /some/path
163 .Ignoring untrusted configuration option paths.interpolated = /some/path/another/path
164 . interpolated = /some/path/another/path
165 136 .Ignoring untrusted configuration option paths.local = /another/path
166 137 . local = /another/path
167 138
@@ -173,10 +144,8 b' Not trusting file foobar from untrusted '
173 144 trusted:
174 145 Ignoring untrusted configuration option foobar.baz = quux
175 146 None
176 /some/path/another/path
177 147 untrusted:
178 148 quux
179 /some/path/another/path
180 149
181 150 # error handling
182 151 # file doesn't exist
@@ -186,26 +155,6 b' quux'
186 155 # parse error
187 156 # different user, different group
188 157 Not trusting file .hg/hgrc from untrusted user abc, group def
189 Ignored: Failed to parse .hg/hgrc
190 File contains no section headers.
191 file: .hg/hgrc, line: 1
192 'foo = bar'
193 # same user, same group
194 raised Abort
195
196 # interpolation error
158 Ignored: config error at .hg/hgrc:1: 'foo'
197 159 # same user, same group
198 # regular config:
199 trusted raised Abort
200 untrusted raised Abort
201 # different user, different group
202 Not trusting file .hg/hgrc from untrusted user abc, group def
203 trusted Ignored: Error in configuration section [foo] parameter 'bar':
204 bad interpolation variable reference '%('
205 None
206 untrusted raised Abort
207 # configitems:
208 trusted Ignored: Error in configuration section [foo]:
209 bad interpolation variable reference '%('
210 []
211 untrusted raised Abort
160 config error at .hg/hgrc:1: 'foo'
@@ -1,7 +1,6 b''
1 1 #!/usr/bin/env python
2 2
3 import ConfigParser
4 from mercurial import ui, util, dispatch
3 from mercurial import ui, util, dispatch, error
5 4
6 5 testui = ui.ui()
7 6 parsed = dispatch._parseconfig(testui, [
@@ -12,19 +11,10 b' parsed = dispatch._parseconfig(testui, ['
12 11 'lists.list2=foo bar baz',
13 12 'lists.list3=alice, bob',
14 13 'lists.list4=foo bar baz alice, bob',
15 'interpolation.value1=hallo',
16 'interpolation.value2=%(value1)s world',
17 'interpolation.value3=%(novalue)s',
18 'interpolation.value4=%(bad)1',
19 'interpolation.value5=%bad2',
20 14 ])
21 15
22 16 print repr(testui.configitems('values'))
23 17 print repr(testui.configitems('lists'))
24 try:
25 print repr(testui.configitems('interpolation'))
26 except util.Abort, inst:
27 print inst
28 18 print "---"
29 19 print repr(testui.config('values', 'string'))
30 20 print repr(testui.config('values', 'bool1'))
@@ -33,7 +23,7 b" print repr(testui.config('values', 'unkn"
33 23 print "---"
34 24 try:
35 25 print repr(testui.configbool('values', 'string'))
36 except util.Abort, inst:
26 except error.ConfigError, inst:
37 27 print inst
38 28 print repr(testui.configbool('values', 'bool1'))
39 29 print repr(testui.configbool('values', 'bool2'))
@@ -54,37 +44,12 b" print repr(testui.configlist('lists', 'u"
54 44 print repr(testui.configlist('lists', 'unknown', 'foo, bar'))
55 45 print repr(testui.configlist('lists', 'unknown', ['foo bar']))
56 46 print repr(testui.configlist('lists', 'unknown', ['foo', 'bar']))
57 print "---"
58 print repr(testui.config('interpolation', 'value1'))
59 print repr(testui.config('interpolation', 'value2'))
60 try:
61 print repr(testui.config('interpolation', 'value3'))
62 except util.Abort, inst:
63 print inst
64 try:
65 print repr(testui.config('interpolation', 'value4'))
66 except util.Abort, inst:
67 print inst
68 try:
69 print repr(testui.config('interpolation', 'value5'))
70 except util.Abort, inst:
71 print inst
72 print "---"
73 47
74 cp = util.configparser()
75 cp.add_section('foo')
76 cp.set('foo', 'bar', 'baz')
77 try:
78 # should fail - keys are case-sensitive
79 cp.get('foo', 'Bar')
80 except ConfigParser.NoOptionError, inst:
81 print inst
48 print repr(testui.config('values', 'String'))
82 49
83 50 def function():
84 51 pass
85 52
86 cp.add_section('hook')
87 53 # values that aren't strings should work
88 cp.set('hook', 'commit', function)
89 f = cp.get('hook', 'commit')
90 print "f %s= function" % (f == function and '=' or '!')
54 testui.setconfig('hook', 'commit', function)
55 print function == testui.config('hook', 'commit')
@@ -1,15 +1,12 b''
1 [('bool1', 'true'), ('bool2', 'false'), ('string', 'string value')]
1 [('string', 'string value'), ('bool1', 'true'), ('bool2', 'false')]
2 2 [('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob')]
3 Error in configuration section [interpolation]:
4 '%' must be followed by '%' or '(', found: '%bad2'
5 3 ---
6 4 'string value'
7 5 'true'
8 6 'false'
9 7 None
10 8 ---
11 Error in configuration section [values] parameter 'string':
12 Not a boolean: string value
9 values.string not a boolean ('string value')
13 10 True
14 11 False
15 12 False
@@ -29,20 +26,5 b' True'
29 26 ['foo', 'bar']
30 27 ['foo bar']
31 28 ['foo', 'bar']
32 ---
33 'hallo'
34 'hallo world'
35 Error in configuration section [interpolation] parameter 'value3':
36 Bad value substitution:
37 section: [interpolation]
38 option : value3
39 key : novalue
40 rawval :
41
42 Error in configuration section [interpolation] parameter 'value4':
43 bad interpolation variable reference '%(bad)1'
44 Error in configuration section [interpolation] parameter 'value5':
45 '%' must be followed by '%' or '(', found: '%bad2'
46 ---
47 No option 'Bar' in section: 'foo'
48 f == function
29 None
30 True
General Comments 0
You need to be logged in to leave comments. Login now