diff --git a/fdroidserver/_yaml.py b/fdroidserver/_yaml.py index 48368198..260f67c0 100644 --- a/fdroidserver/_yaml.py +++ b/fdroidserver/_yaml.py @@ -38,3 +38,27 @@ yaml = ruamel.yaml.YAML(typ='safe') yaml.version = (1, 2) yaml_dumper = ruamel.yaml.YAML(typ='rt') + + +def config_dump(config, fp=None): + """Dump config data in YAML 1.2 format without headers. + + This outputs YAML in a string that is suitable for use in regexps + and string replacements, as well as complete files. It is therefore + explicitly set up to avoid writing out headers and footers. + + This is modeled after PyYAML's yaml.dump(), which can dump to a file + or return a string. + + https://yaml.dev/doc/ruamel.yaml/example/#Output_of_%60dump()%60_as_a_string + + """ + dumper = ruamel.yaml.YAML(typ='rt') + dumper.default_flow_style = False + dumper.explicit_start = False + dumper.explicit_end = False + if fp is None: + with ruamel.yaml.compat.StringIO() as fp: + dumper.dump(config, fp) + return fp.getvalue() + dumper.dump(config, fp) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 320cffe4..80806c6e 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -39,7 +39,6 @@ import sys import re import ast import gzip -import ruamel.yaml import shutil import stat import subprocess @@ -67,7 +66,7 @@ from zipfile import ZipFile import fdroidserver.metadata from fdroidserver import _ -from fdroidserver._yaml import yaml, yaml_dumper +from fdroidserver._yaml import yaml, config_dump from fdroidserver.exception import FDroidException, VCSException, NoSubmodulesException, \ BuildException, VerificationException, MetaDataException from .asynchronousfilereader import AsynchronousFileReader @@ -4230,9 +4229,7 @@ def write_to_config(thisconfig, key, value=None): lines[-1] += '\n' pattern = re.compile(r'^[\s#]*' + key + r':.*\n') - with ruamel.yaml.compat.StringIO() as fp: - yaml_dumper.dump({key: value}, fp) - repl = fp.getvalue() + repl = config_dump({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. diff --git a/tests/test_common.py b/tests/test_common.py index f5ffbfbe..3513bf53 100755 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -31,7 +31,7 @@ import fdroidserver.common import fdroidserver.metadata from .shared_test_code import TmpCwd, mkdtemp from fdroidserver.common import ANTIFEATURES_CONFIG_NAME, CATEGORIES_CONFIG_NAME -from fdroidserver._yaml import yaml, yaml_dumper +from fdroidserver._yaml import yaml, yaml_dumper, config_dump from fdroidserver.exception import FDroidException, VCSException,\ MetaDataException, VerificationException from fdroidserver.looseversion import LooseVersion @@ -1933,7 +1933,7 @@ class CommonTest(unittest.TestCase): os.chdir(self.tmpdir) teststr = '/πÇÇ现代通用字-български-عربي1/ö/yml' with open(fdroidserver.common.CONFIG_FILE, 'w', encoding='utf-8') as fp: - yaml_dumper.dump({'apksigner': teststr}, fp) + config_dump({'apksigner': teststr}, fp) self.assertTrue(os.path.exists(fdroidserver.common.CONFIG_FILE)) config = fdroidserver.common.read_config() self.assertEqual(teststr, config.get('apksigner')) @@ -2633,7 +2633,7 @@ class CommonTest(unittest.TestCase): ' -providerClass sun.security.pkcs11.SunPKCS11' ' -providerArg opensc-fdroid.cfg' } - yaml_dumper.dump(d, fp) + config_dump(d, fp) config = fdroidserver.common.read_config() fdroidserver.common.config = config self.assertTrue(isinstance(d['smartcardoptions'], str)) diff --git a/tests/test_lint.py b/tests/test_lint.py index 820c80d6..c9e7b3f4 100755 --- a/tests/test_lint.py +++ b/tests/test_lint.py @@ -12,7 +12,7 @@ from .shared_test_code import mkdtemp import fdroidserver.common import fdroidserver.lint import fdroidserver.metadata -from fdroidserver._yaml import yaml_dumper +from fdroidserver._yaml import config_dump basedir = Path(__file__).parent @@ -365,13 +365,13 @@ class LintTest(unittest.TestCase): def test_lint_config_basic_mirrors_yml(self): os.chdir(self.testdir) with Path('mirrors.yml').open('w') as fp: - yaml_dumper.dump([{'url': 'https://example.com/fdroid/repo'}], fp) + config_dump([{'url': 'https://example.com/fdroid/repo'}], fp) self.assertTrue(fdroidserver.lint.lint_config('mirrors.yml')) def test_lint_config_mirrors_yml_kenya_countryCode(self): os.chdir(self.testdir) with Path('mirrors.yml').open('w') as fp: - yaml_dumper.dump( + config_dump( [{'url': 'https://foo.com/fdroid/repo', 'countryCode': 'KE'}], fp ) self.assertTrue(fdroidserver.lint.lint_config('mirrors.yml')) @@ -380,7 +380,7 @@ class LintTest(unittest.TestCase): """WV is "indeterminately reserved" so it should never be used.""" os.chdir(self.testdir) with Path('mirrors.yml').open('w') as fp: - yaml_dumper.dump( + config_dump( [{'url': 'https://foo.com/fdroid/repo', 'countryCode': 'WV'}], fp ) self.assertFalse(fdroidserver.lint.lint_config('mirrors.yml')) @@ -389,7 +389,7 @@ class LintTest(unittest.TestCase): """Only ISO 3166-1 alpha 2 are supported""" os.chdir(self.testdir) with Path('mirrors.yml').open('w') as fp: - yaml_dumper.dump( + config_dump( [{'url': 'https://de.com/fdroid/repo', 'countryCode': 'DEU'}], fp ) self.assertFalse(fdroidserver.lint.lint_config('mirrors.yml')) @@ -398,7 +398,7 @@ class LintTest(unittest.TestCase): """WV is "indeterminately reserved" so it should never be used.""" os.chdir(self.testdir) with Path('mirrors.yml').open('w') as fp: - yaml_dumper.dump( + config_dump( [ {'url': 'https://bar.com/fdroid/repo', 'countryCode': 'BA'}, {'url': 'https://foo.com/fdroid/repo', 'countryCode': 'FO'},