chiark / gitweb /
Merge branch '265-fdroid-init-generates-double-entries-in-config-py' into 'master'
authorHans-Christoph Steiner <hans@guardianproject.info>
Mon, 3 Apr 2017 10:13:20 +0000 (10:13 +0000)
committerHans-Christoph Steiner <hans@guardianproject.info>
Mon, 3 Apr 2017 10:13:20 +0000 (10:13 +0000)
avoid duplicate value assignments in config files

Closes #265

See merge request !241

fdroidserver/common.py
tests/common.TestCase

index c11119392f2d77d4d31fe958836e34aded88ba4d..a9778943e5931f0ce55ffd26e1175f6a5e794e37 100644 (file)
@@ -2213,24 +2213,55 @@ def get_cert_fingerprint(pubkey):
     return " ".join(ret)
 
 
-def write_to_config(thisconfig, key, value=None):
-    '''write a key/value to the local config.py'''
+def write_to_config(thisconfig, key, value=None, config_file=None):
+    '''write a key/value to the local config.py
+
+    NOTE: only supports writing string variables.
+
+    :param thisconfig: config dictionary
+    :param key: variable name in config.py to be overwritten/added
+    :param value: optional value to be written, instead of fetched
+        from 'thisconfig' dictionary.
+    '''
     if value is None:
         origkey = key + '_orig'
         value = thisconfig[origkey] if origkey in thisconfig else thisconfig[key]
-    with open('config.py', 'r', encoding='utf8') as f:
-        data = f.read()
-    pattern = '\n[\s#]*' + key + '\s*=\s*"[^"]*"'
-    repl = '\n' + key + ' = "' + value + '"'
-    data = re.sub(pattern, repl, data)
-    # if this key is not in the file, append it
-    if not re.match('\s*' + key + '\s*=\s*"', data):
-        data += repl
+    cfg = config_file if config_file else 'config.py'
+
+    # load config file
+    with open(cfg, 'r', encoding="utf-8") as f:
+        lines = f.readlines()
+
     # make sure the file ends with a carraige return
-    if not re.match('\n$', data):
-        data += '\n'
-    with open('config.py', 'w', encoding='utf8') as f:
-        f.writelines(data)
+    if len(lines) > 0:
+        if not lines[-1].endswith('\n'):
+            lines[-1] += '\n'
+
+    # regex for finding and replacing python string variable
+    # definitions/initializations
+    pattern = re.compile('^[\s#]*' + key + '\s*=\s*"[^"]*"')
+    repl = key + ' = "' + value + '"'
+    pattern2 = re.compile('^[\s#]*' + key + "\s*=\s*'[^']*'")
+    repl2 = key + " = '" + value + "'"
+
+    # If we replaced this line once, we make sure won't be a
+    # second instance of this line for this key in the document.
+    didRepl = False
+    # edit config file
+    with open(cfg, 'w', encoding="utf-8") as f:
+        for line in lines:
+            if pattern.match(line) or pattern2.match(line):
+                if not didRepl:
+                    line = pattern.sub(repl, line)
+                    line = pattern2.sub(repl2, line)
+                    f.write(line)
+                    didRepl = True
+            else:
+                f.write(line)
+        if not didRepl:
+            f.write('\n')
+            f.write(repl)
+            f.write('\n')
 
 
 def parse_xml(path):
index 50cbd3204b906f92767e22c2325b804b32dddccc..d5978405e1f547fe83db89009e548873e04d5946 100755 (executable)
@@ -10,9 +10,9 @@ import shutil
 import sys
 import tempfile
 import unittest
+import textwrap
 from zipfile import ZipFile
 
-import fdroidserver.signindex
 
 localmodule = os.path.realpath(
     os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
@@ -20,6 +20,7 @@ print('localmodule: ' + localmodule)
 if localmodule not in sys.path:
     sys.path.insert(0, localmodule)
 
+import fdroidserver.signindex
 import fdroidserver.common
 import fdroidserver.metadata
 
@@ -233,6 +234,53 @@ class CommonTest(unittest.TestCase):
         self.assertFalse(fdroidserver.common.verify_apk_signature(twosigapk))
         self.assertIsNone(fdroidserver.common.verify_apks(sourceapk, twosigapk, tmpdir))
 
+    def test_write_to_config(self):
+        with tempfile.TemporaryDirectory() as tmpPath:
+            cfgPath = os.path.join(tmpPath, 'config.py')
+            with open(cfgPath, 'w', encoding='utf-8') as f:
+                f.write(textwrap.dedent("""\
+                    # abc
+                    # test = 'example value'
+                    default_me= '%%%'
+
+                    # comment
+                    do_not_touch = "good value"
+                    default_me="!!!"
+
+                    key="123"    # inline"""))
+
+            cfg = {'key': '111', 'default_me_orig': 'orig'}
+            fdroidserver.common.write_to_config(cfg, 'key', config_file=cfgPath)
+            fdroidserver.common.write_to_config(cfg, 'default_me', config_file=cfgPath)
+            fdroidserver.common.write_to_config(cfg, 'test', value='test value', config_file=cfgPath)
+            fdroidserver.common.write_to_config(cfg, 'new_key', value='new', config_file=cfgPath)
+
+            with open(cfgPath, 'r', encoding='utf-8') as f:
+                self.assertEqual(f.read(), textwrap.dedent("""\
+                    # abc
+                    test = 'test value'
+                    default_me = 'orig'
+
+                    # comment
+                    do_not_touch = "good value"
+
+                    key = "111"    # inline
+
+                    new_key = "new"
+                    """))
+
+    def test_write_to_config_when_empty(self):
+        with tempfile.TemporaryDirectory() as tmpPath:
+            cfgPath = os.path.join(tmpPath, 'config.py')
+            with open(cfgPath, 'w') as f:
+                pass
+            fdroidserver.common.write_to_config({}, 'key', 'val', cfgPath)
+            with open(cfgPath, 'r', encoding='utf-8') as f:
+                self.assertEqual(f.read(), textwrap.dedent("""\
+
+                key = "val"
+                """))
+
 
 if __name__ == "__main__":
     parser = optparse.OptionParser()