Browse Source

add inifile add/remove code

Sean Dague 2 years ago
parent
commit
0c14c58913
4 changed files with 259 additions and 1 deletions
  1. 70
    0
      devstack/dsconf.py
  2. 90
    0
      devstack/tests/test_ini_add.py
  3. 98
    0
      devstack/tests/test_ini_remove.py
  4. 1
    1
      test-requirements.txt

+ 70
- 0
devstack/dsconf.py View File

@@ -0,0 +1,70 @@
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
+
19
+import re
20
+import shutil
21
+import tempfile
22
+
23
+
24
+class IniFile(object):
25
+    """Class for manipulating ini files in place."""
26
+
27
+    def __init__(self, fname):
28
+        self.fname = fname
29
+
30
+    def add(self, section, name, value):
31
+        """add a key / value to an ini file in a section.
32
+
33
+        The new key value will be added at the beginning of the
34
+        section, if no section is found a new section and key value
35
+        will be added to the end of the file.
36
+        """
37
+        temp = tempfile.NamedTemporaryFile(mode='r')
38
+        shutil.copyfile(self.fname, temp.name)
39
+        found = False
40
+        with open(temp.name) as reader:
41
+            with open(self.fname, "w") as writer:
42
+                for line in reader.readlines():
43
+                    writer.write(line)
44
+                    m = re.match("\[([^\[\]]+)\]", line)
45
+                    if m and m.group(1) == section:
46
+                        found = True
47
+                        writer.write("%s = %s\n" % (name, value))
48
+                if not found:
49
+                    writer.write("[%s]\n" % section)
50
+                    writer.write("%s = %s\n" % (name, value))
51
+
52
+    def remove(self, section, name):
53
+        """remove a key / value from an ini file in a section."""
54
+
55
+        temp = tempfile.NamedTemporaryFile(mode='r')
56
+        shutil.copyfile(self.fname, temp.name)
57
+        current_section = ""
58
+        with open(temp.name) as reader:
59
+            with open(self.fname, "w") as writer:
60
+                for line in reader.readlines():
61
+                    m = re.match("\[([^\[\]]+)\]", line)
62
+                    if m:
63
+                        current_section = m.group(1)
64
+                    if current_section == section:
65
+                        if re.match("%s\s*\=" % name, line):
66
+                            continue
67
+                        else:
68
+                            writer.write(line)
69
+                    else:
70
+                        writer.write(line)

+ 90
- 0
devstack/tests/test_ini_add.py View File

@@ -0,0 +1,90 @@
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
+
19
+import fixtures
20
+import testtools
21
+
22
+from devstack import dsconf
23
+
24
+
25
+BASIC = """[default]
26
+a = b
27
+c = d
28
+[second]
29
+e = f
30
+g = h
31
+"""
32
+
33
+RESULT1 = """[default]
34
+s = t
35
+a = b
36
+c = d
37
+[second]
38
+e = f
39
+g = h
40
+"""
41
+
42
+RESULT2 = """[default]
43
+a = b
44
+c = d
45
+[second]
46
+s = t
47
+e = f
48
+g = h
49
+"""
50
+
51
+RESULT3 = """[default]
52
+a = b
53
+c = d
54
+[second]
55
+e = f
56
+g = h
57
+[new]
58
+s = t
59
+"""
60
+
61
+
62
+class TestIniAdd(testtools.TestCase):
63
+
64
+    def setUp(self):
65
+        super(TestIniAdd, self).setUp()
66
+        self._path = self.useFixture(fixtures.TempDir()).path
67
+        self._path += "/test.ini"
68
+        with open(self._path, "w") as f:
69
+            f.write(BASIC)
70
+
71
+    def test_add_ini_default(self):
72
+        conf = dsconf.IniFile(self._path)
73
+        conf.add("default", "s", "t")
74
+        with open(self._path) as f:
75
+            content = f.read()
76
+            self.assertEqual(content, RESULT1)
77
+
78
+    def test_add_ini_second(self):
79
+        conf = dsconf.IniFile(self._path)
80
+        conf.add("second", "s", "t")
81
+        with open(self._path) as f:
82
+            content = f.read()
83
+            self.assertEqual(content, RESULT2)
84
+
85
+    def test_add_ini_new(self):
86
+        conf = dsconf.IniFile(self._path)
87
+        conf.add("new", "s", "t")
88
+        with open(self._path) as f:
89
+            content = f.read()
90
+            self.assertEqual(content, RESULT3)

+ 98
- 0
devstack/tests/test_ini_remove.py View File

@@ -0,0 +1,98 @@
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
+
19
+import fixtures
20
+import testtools
21
+
22
+from devstack import dsconf
23
+
24
+
25
+BASIC = """[default]
26
+a = b
27
+c = d
28
+[second]
29
+e = f
30
+g = h
31
+[new]
32
+s = t
33
+"""
34
+
35
+RESULT1 = """[default]
36
+c = d
37
+[second]
38
+e = f
39
+g = h
40
+[new]
41
+s = t
42
+"""
43
+
44
+RESULT2 = """[default]
45
+a = b
46
+c = d
47
+[second]
48
+e = f
49
+[new]
50
+s = t
51
+"""
52
+
53
+RESULT3 = """[default]
54
+a = b
55
+c = d
56
+[second]
57
+e = f
58
+g = h
59
+[new]
60
+"""
61
+
62
+
63
+class TestIniRemove(testtools.TestCase):
64
+
65
+    def setUp(self):
66
+        super(TestIniRemove, self).setUp()
67
+        self._path = self.useFixture(fixtures.TempDir()).path
68
+        self._path += "/test.ini"
69
+        with open(self._path, "w") as f:
70
+            f.write(BASIC)
71
+
72
+    def test_remove_ini_default(self):
73
+        conf = dsconf.IniFile(self._path)
74
+        conf.remove("default", "a")
75
+        with open(self._path) as f:
76
+            content = f.read()
77
+            self.assertEqual(content, RESULT1)
78
+
79
+    def test_remove_ini_second(self):
80
+        conf = dsconf.IniFile(self._path)
81
+        conf.remove("second", "g")
82
+        with open(self._path) as f:
83
+            content = f.read()
84
+            self.assertEqual(content, RESULT2)
85
+
86
+    def test_remove_ini_new(self):
87
+        conf = dsconf.IniFile(self._path)
88
+        conf.remove("new", "s")
89
+        with open(self._path) as f:
90
+            content = f.read()
91
+            self.assertEqual(content, RESULT3)
92
+
93
+    def test_remove_ini_none(self):
94
+        conf = dsconf.IniFile(self._path)
95
+        conf.remove("default", "s")
96
+        with open(self._path) as f:
97
+            content = f.read()
98
+            self.assertEqual(content, BASIC)

+ 1
- 1
test-requirements.txt View File

@@ -7,10 +7,10 @@ hacking>=0.12.0,<0.13 # Apache-2.0
7 7
 coverage>=4.0 # Apache-2.0
8 8
 python-subunit>=0.0.18 # Apache-2.0/BSD
9 9
 sphinx>=1.2.1,!=1.3b1,<1.4 # BSD
10
+fixtures
10 11
 oslosphinx>=4.7.0 # Apache-2.0
11 12
 oslotest>=1.10.0 # Apache-2.0
12 13
 testrepository>=0.0.18  # Apache-2.0/BSD
13
-testscenarios>=0.4  # Apache-2.0/BSD
14 14
 testtools>=1.4.0 # MIT
15 15
 
16 16
 # releasenotes

Loading…
Cancel
Save