summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Dague <sean@dague.net>2017-01-16 10:04:51 -0500
committerSean Dague <sean@dague.net>2017-01-16 10:04:51 -0500
commit6971e39e94c3a16d62d55eddf764cd13d41d6682 (patch)
treecaeb158e95297caaf62c6a109ef0c9956c722aea
parent0c14c589133db28f041c641eea08bd2f25fb610b (diff)
Add comment, set, has methods
-rw-r--r--devstack/dsconf.py45
-rw-r--r--devstack/tests/test_ini_comment.py101
-rw-r--r--devstack/tests/test_ini_set.py112
3 files changed, 254 insertions, 4 deletions
diff --git a/devstack/dsconf.py b/devstack/dsconf.py
index b31e221..f9ba066 100644
--- a/devstack/dsconf.py
+++ b/devstack/dsconf.py
@@ -27,6 +27,21 @@ class IniFile(object):
27 def __init__(self, fname): 27 def __init__(self, fname):
28 self.fname = fname 28 self.fname = fname
29 29
30 def has(self, section, name):
31 """Returns True if section has a key that is name"""
32
33 current_section = ""
34 with open(self.fname) as reader:
35 for line in reader.readlines():
36 m = re.match("\[([^\[\]]+)\]", line)
37 if m:
38 current_section = m.group(1)
39 if current_section == section:
40 if re.match("%s\s*\=" % name, line):
41 return True
42 return False
43
44
30 def add(self, section, name, value): 45 def add(self, section, name, value):
31 """add a key / value to an ini file in a section. 46 """add a key / value to an ini file in a section.
32 47
@@ -49,9 +64,7 @@ class IniFile(object):
49 writer.write("[%s]\n" % section) 64 writer.write("[%s]\n" % section)
50 writer.write("%s = %s\n" % (name, value)) 65 writer.write("%s = %s\n" % (name, value))
51 66
52 def remove(self, section, name): 67 def _at_existing_key(self, section, name, func):
53 """remove a key / value from an ini file in a section."""
54
55 temp = tempfile.NamedTemporaryFile(mode='r') 68 temp = tempfile.NamedTemporaryFile(mode='r')
56 shutil.copyfile(self.fname, temp.name) 69 shutil.copyfile(self.fname, temp.name)
57 current_section = "" 70 current_section = ""
@@ -63,8 +76,32 @@ class IniFile(object):
63 current_section = m.group(1) 76 current_section = m.group(1)
64 if current_section == section: 77 if current_section == section:
65 if re.match("%s\s*\=" % name, line): 78 if re.match("%s\s*\=" % name, line):
66 continue 79 # run function with writer and found line
80 func(writer, line)
67 else: 81 else:
68 writer.write(line) 82 writer.write(line)
69 else: 83 else:
70 writer.write(line) 84 writer.write(line)
85
86
87 def remove(self, section, name):
88 """remove a key / value from an ini file in a section."""
89 def _do_remove(writer, line):
90 pass
91
92 self._at_existing_key(section, name, _do_remove)
93
94
95 def comment(self, section, name):
96 def _do_comment(writer, line):
97 writer.write("# %s" % line)
98
99 self._at_existing_key(section, name, _do_comment)
100
101 def set(self, section, name, value):
102 def _do_set(writer, line):
103 writer.write("%s = %s\n" % (name, value))
104 if self.has(section, name):
105 self._at_existing_key(section, name, _do_set)
106 else:
107 self.add(section, name, value)
diff --git a/devstack/tests/test_ini_comment.py b/devstack/tests/test_ini_comment.py
new file mode 100644
index 0000000..db1a28a
--- /dev/null
+++ b/devstack/tests/test_ini_comment.py
@@ -0,0 +1,101 @@
1# Copyright 2017 IBM
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15# Implementation of ini add / remove for devstack. We don't use the
16# python ConfigFile parser because that ends up rewriting the entire
17# file and doesn't ensure comments remain.
18
19import fixtures
20import testtools
21
22from devstack import dsconf
23
24
25BASIC = """[default]
26a = b
27c = d
28[second]
29e = f
30g = h
31[new]
32s = t
33"""
34
35RESULT1 = """[default]
36# a = b
37c = d
38[second]
39e = f
40g = h
41[new]
42s = t
43"""
44
45RESULT2 = """[default]
46a = b
47c = d
48[second]
49e = f
50# g = h
51[new]
52s = t
53"""
54
55RESULT3 = """[default]
56a = b
57c = d
58[second]
59e = f
60g = h
61[new]
62# s = t
63"""
64
65
66class TestIniComment(testtools.TestCase):
67
68 def setUp(self):
69 super(TestIniComment, self).setUp()
70 self._path = self.useFixture(fixtures.TempDir()).path
71 self._path += "/test.ini"
72 with open(self._path, "w") as f:
73 f.write(BASIC)
74
75 def test_comment_ini_default(self):
76 conf = dsconf.IniFile(self._path)
77 conf.comment("default", "a")
78 with open(self._path) as f:
79 content = f.read()
80 self.assertEqual(content, RESULT1)
81
82 def test_comment_ini_second(self):
83 conf = dsconf.IniFile(self._path)
84 conf.comment("second", "g")
85 with open(self._path) as f:
86 content = f.read()
87 self.assertEqual(content, RESULT2)
88
89 def test_comment_ini_new(self):
90 conf = dsconf.IniFile(self._path)
91 conf.comment("new", "s")
92 with open(self._path) as f:
93 content = f.read()
94 self.assertEqual(content, RESULT3)
95
96 def test_comment_ini_none(self):
97 conf = dsconf.IniFile(self._path)
98 conf.comment("default", "s")
99 with open(self._path) as f:
100 content = f.read()
101 self.assertEqual(content, BASIC)
diff --git a/devstack/tests/test_ini_set.py b/devstack/tests/test_ini_set.py
new file mode 100644
index 0000000..383bfc7
--- /dev/null
+++ b/devstack/tests/test_ini_set.py
@@ -0,0 +1,112 @@
1# Copyright 2017 IBM
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15# Implementation of ini add / remove for devstack. We don't use the
16# python ConfigFile parser because that ends up rewriting the entire
17# file and doesn't ensure comments remain.
18
19import fixtures
20import testtools
21
22from devstack import dsconf
23
24
25BASIC = """[default]
26a = b
27c = d
28[second]
29e = f
30g = h
31[new]
32s = t
33"""
34
35RESULT1 = """[default]
36a = 2
37c = d
38[second]
39e = f
40g = h
41[new]
42s = t
43"""
44
45RESULT2 = """[default]
46a = b
47c = d
48[second]
49e = f
50g = 2
51[new]
52s = t
53"""
54
55RESULT3 = """[default]
56a = b
57c = d
58[second]
59e = f
60g = h
61[new]
62s = 2
63"""
64
65RESULT4 = """[default]
66s = 2
67a = b
68c = d
69[second]
70e = f
71g = h
72[new]
73s = t
74"""
75
76
77class TestIniSet(testtools.TestCase):
78
79 def setUp(self):
80 super(TestIniSet, self).setUp()
81 self._path = self.useFixture(fixtures.TempDir()).path
82 self._path += "/test.ini"
83 with open(self._path, "w") as f:
84 f.write(BASIC)
85
86 def test_set_ini_default(self):
87 conf = dsconf.IniFile(self._path)
88 conf.set("default", "a", "2")
89 with open(self._path) as f:
90 content = f.read()
91 self.assertEqual(content, RESULT1)
92
93 def test_set_ini_second(self):
94 conf = dsconf.IniFile(self._path)
95 conf.set("second", "g", "2")
96 with open(self._path) as f:
97 content = f.read()
98 self.assertEqual(content, RESULT2)
99
100 def test_set_ini_new(self):
101 conf = dsconf.IniFile(self._path)
102 conf.set("new", "s", "2")
103 with open(self._path) as f:
104 content = f.read()
105 self.assertEqual(content, RESULT3)
106
107 def test_set_ini_none(self):
108 conf = dsconf.IniFile(self._path)
109 conf.set("default", "s", "2")
110 with open(self._path) as f:
111 content = f.read()
112 self.assertEqual(content, RESULT4)