From 4711b632b8a78e8b0273609b29ece3b6c7c81549 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 24 Apr 2023 23:15:18 +0200 Subject: [PATCH] metadata: _normalize_type_int to handle exceptions --- fdroidserver/metadata.py | 31 +++++++++++++++------- tests/metadata.TestCase | 57 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/fdroidserver/metadata.py b/fdroidserver/metadata.py index 7f406e7d..6d105816 100644 --- a/fdroidserver/metadata.py +++ b/fdroidserver/metadata.py @@ -884,6 +884,21 @@ def parse_localized_antifeatures(app): app['AntiFeatures'][f.stem][locale] = f.read_text() +def _normalize_type_int(k, v): + """Normalize anything that can be reliably converted to an integer.""" + if isinstance(v, int) and not isinstance(v, bool): + return v + if v is None: + return None + if isinstance(v, str): + try: + return int(v) + except ValueError: + pass + msg = _('{build_flag} must be an integer, found: {value}') + _warn_or_exception(msg.format(build_flag=k, value=v)) + + def _normalize_type_string(v): """Normalize any data to TYPE_STRING. @@ -980,8 +995,9 @@ def post_parse_yaml_metadata(yamldata): elif v: yamldata[k] = [str(i) for i in v] elif _fieldtype == TYPE_INT: - if v: - yamldata[k] = int(v) + v = _normalize_type_int(k, v) + if v or v == 0: + yamldata[k] = v elif _fieldtype == TYPE_STRING: if v or v == 0: yamldata[k] = _normalize_type_string(v) @@ -1005,14 +1021,9 @@ def post_parse_yaml_metadata(yamldata): if v or v == 0: build[k] = _normalize_type_string(v) elif _flagtype == TYPE_INT: - build[k] = v - # versionCode must be int - if not isinstance(v, int): - _warn_or_exception( - _('{build_flag} must be an integer, found: {value}').format( - build_flag=k, value=v - ) - ) + v = _normalize_type_int(k, v) + if v or v == 0: + build[k] = v elif _flagtype in (TYPE_LIST, TYPE_SCRIPT): if isinstance(v, str) or isinstance(v, int): build[k] = [_normalize_type_string(v)] diff --git a/tests/metadata.TestCase b/tests/metadata.TestCase index 2da4b16c..bb41a3a2 100755 --- a/tests/metadata.TestCase +++ b/tests/metadata.TestCase @@ -355,6 +355,27 @@ class MetadataTest(unittest.TestCase): metadata._normalize_type_stringmap('AntiFeatures', ['Ads', 'Tracking']), ) + def test_normalize_type_int(self): + """TYPE_INT should be an int whenever possible.""" + self.assertEqual(0, metadata._normalize_type_int('key', 0)) + self.assertEqual(1, metadata._normalize_type_int('key', 1)) + self.assertEqual(-5, metadata._normalize_type_int('key', -5)) + self.assertEqual(0, metadata._normalize_type_int('key', '0')) + self.assertEqual(1, metadata._normalize_type_int('key', '1')) + self.assertEqual(-5, metadata._normalize_type_int('key', '-5')) + self.assertEqual( + 12345678901234567890, + metadata._normalize_type_int('key', 12345678901234567890), + ) + + def test_normalize_type_int_fails(self): + with self.assertRaises(MetaDataException): + metadata._normalize_type_int('key', '1a') + with self.assertRaises(MetaDataException): + metadata._normalize_type_int('key', 1.1) + with self.assertRaises(MetaDataException): + metadata._normalize_type_int('key', True) + def test_post_parse_yaml_metadata(self): yamldata = dict() metadata.post_parse_yaml_metadata(yamldata) @@ -383,7 +404,7 @@ class MetadataTest(unittest.TestCase): yamldata, ) - build['versionCode'] = '1' + build['versionCode'] = '1a' self.assertRaises( fdroidserver.exception.MetaDataException, fdroidserver.metadata.post_parse_yaml_metadata, @@ -1124,6 +1145,34 @@ class MetadataTest(unittest.TestCase): }, ) + def test_parse_yaml_build_type_int_fail(self): + mf = io.StringIO('Builds: [{versionCode: 1a}]') + with self.assertRaises(MetaDataException): + fdroidserver.metadata.parse_yaml_metadata(mf) + + def test_parse_yaml_int_strict_typing_fails(self): + """Things that cannot be preserved when parsing as YAML.""" + mf = io.StringIO('Builds: [{versionCode: 1, rm: 0xf}]') + self.assertEqual( + {'Builds': [{'rm': ['15'], 'versionCode': 1}]}, # 15 != 0xf + fdroidserver.metadata.parse_yaml_metadata(mf), + ) + mf = io.StringIO('Builds: [{versionCode: 1, rm: 0x010}]') + self.assertEqual( + {'Builds': [{'rm': ['16'], 'versionCode': 1}]}, # 16 != 0x010 + fdroidserver.metadata.parse_yaml_metadata(mf), + ) + mf = io.StringIO('Builds: [{versionCode: 1, rm: 0o015}]') + self.assertEqual( + {'Builds': [{'rm': ['13'], 'versionCode': 1}]}, # 13 != 0o015 + fdroidserver.metadata.parse_yaml_metadata(mf), + ) + mf = io.StringIO('Builds: [{versionCode: 1, rm: 10_000}]') + self.assertEqual( + {'Builds': [{'rm': ['10000'], 'versionCode': 1}]}, # 10000 != 10_000 + fdroidserver.metadata.parse_yaml_metadata(mf), + ) + def test_write_yaml_1_line_scripts_as_string(self): mf = io.StringIO() app = fdroidserver.metadata.App() @@ -2137,7 +2186,8 @@ class PostMetadataParseTest(unittest.TestCase): self.assertEqual(*self._post_metadata_parse_app_list(False, False)) self.assertEqual(*self._post_metadata_parse_app_string(False, 'false')) self.assertEqual(*self._post_metadata_parse_build_bool(False, False)) - self.assertEqual(*self._post_metadata_parse_build_int(False, False)) + with self.assertRaises(MetaDataException): + self._post_metadata_parse_build_int(False, MetaDataException) self.assertEqual(*self._post_metadata_parse_build_list(False, ['false'])) self.assertEqual(*self._post_metadata_parse_build_script(False, ['false'])) self.assertEqual(*self._post_metadata_parse_build_string(False, 'false')) @@ -2147,7 +2197,8 @@ class PostMetadataParseTest(unittest.TestCase): self._post_metadata_parse_app_list(True, TypeError) self.assertEqual(*self._post_metadata_parse_app_string(True, 'true')) self.assertEqual(*self._post_metadata_parse_build_bool(True, True)) - self.assertEqual(*self._post_metadata_parse_build_int(True, True)) + with self.assertRaises(MetaDataException): + self._post_metadata_parse_build_int(True, MetaDataException) self.assertEqual(*self._post_metadata_parse_build_list(True, ['true'])) self.assertEqual(*self._post_metadata_parse_build_script(True, ['true'])) self.assertEqual(*self._post_metadata_parse_build_string(True, 'true'))