metadata: remove strange app arg construct from parse_yaml_metadata()

My guess is that this is some kind of vestige of the old code structure,
back when there was .txt and .yml formats.  This makes it a normal Python
function: input as arg, return value is the result.
This commit is contained in:
Hans-Christoph Steiner 2023-04-20 17:43:56 +02:00
parent 41972e6525
commit c0ae09e0df
2 changed files with 46 additions and 22 deletions

View file

@ -675,23 +675,31 @@ def post_metadata_parse(app):
def parse_metadata(metadatapath): def parse_metadata(metadatapath):
"""Parse metadata file, also checking the source repo for .fdroid.yml. """Parse metadata file, also checking the source repo for .fdroid.yml.
This function finds the relevant files, gets them parsed, converts
dicts into App and Build instances, and combines the results into
a single App instance.
If this is a metadata file from fdroiddata, it will first load the If this is a metadata file from fdroiddata, it will first load the
source repo type and URL from fdroiddata, then read .fdroid.yml if source repo type and URL from fdroiddata, then read .fdroid.yml if
it exists, then include the rest of the metadata as specified in it exists, then include the rest of the metadata as specified in
fdroiddata, so that fdroiddata has precedence over the metadata in fdroiddata, so that fdroiddata has precedence over the metadata in
the source code. the source code.
.fdroid.yml is embedded in the app's source repo, so it is
"user-generated". That means that it can have weird things in it
that need to be removed so they don't break the overall process,
e.g. if the upstream developer includes some broken field, it can
be overridden in the metadata file.
""" """
metadatapath = Path(metadatapath) metadatapath = Path(metadatapath)
app = App() app = App()
app.metadatapath = metadatapath.as_posix() app.metadatapath = metadatapath.as_posix()
name = metadatapath.stem if metadatapath.stem != '.fdroid':
if name != '.fdroid': app.id = metadatapath.stem
app.id = name
if metadatapath.suffix == '.yml': if metadatapath.suffix == '.yml':
with metadatapath.open('r', encoding='utf-8') as mf: with metadatapath.open('r', encoding='utf-8') as mf:
parse_yaml_metadata(mf, app) app.update(parse_yaml_metadata(mf))
else: else:
_warn_or_exception(_('Unknown metadata format: {path} (use: *.yml)') _warn_or_exception(_('Unknown metadata format: {path} (use: *.yml)')
.format(path=metadatapath)) .format(path=metadatapath))
@ -727,15 +735,18 @@ def parse_metadata(metadatapath):
return app return app
def parse_yaml_metadata(mf, app): def parse_yaml_metadata(mf):
"""Parse the .yml file and post-process it. """Parse the .yml file and post-process it.
This function handles parsing a metadata YAML file and converting
all the various data types into a consistent internal
representation. The results are meant to update an existing App
instance or used as a plain dict.
Clean metadata .yml files can be used directly, but in order to Clean metadata .yml files can be used directly, but in order to
make a better user experience for people editing .yml files, there make a better user experience for people editing .yml files, there
is post processing. .fdroid.yml is embedded in the app's source is post processing. That makes the parsing perform something like
repo, so it is "user-generated". That means that it can have Strict YAML.
weird things in it that need to be removed so they don't break the
overall process.
""" """
try: try:
@ -747,6 +758,9 @@ def parse_yaml_metadata(mf, app):
+ common.run_yamllint(mf.name, indent=4), + common.run_yamllint(mf.name, indent=4),
cause=e) cause=e)
if yamldata is None or yamldata == '':
return dict()
deprecated_in_yaml = ['Provides'] deprecated_in_yaml = ['Provides']
if yamldata: if yamldata:
@ -775,8 +789,7 @@ def parse_yaml_metadata(mf, app):
_warn_or_exception(msg.format(build_flag=build_flag, path=mf.name)) _warn_or_exception(msg.format(build_flag=build_flag, path=mf.name))
post_parse_yaml_metadata(yamldata) post_parse_yaml_metadata(yamldata)
app.update(yamldata) return yamldata
return app
def post_parse_yaml_metadata(yamldata): def post_parse_yaml_metadata(yamldata):

View file

@ -362,6 +362,21 @@ class MetadataTest(unittest.TestCase):
allappids.append(appid) allappids.append(appid)
self.assertEqual(randomlist, allappids) self.assertEqual(randomlist, allappids)
def test_parse_yaml_metadata_0size_file(self):
mf = io.StringIO('')
mf.name = 'mock_filename.yaml'
self.assertEqual(fdroidserver.metadata.parse_yaml_metadata(mf), dict())
def test_parse_yaml_metadata_empty_dict_file(self):
mf = io.StringIO('{}')
mf.name = 'mock_filename.yaml'
self.assertEqual(fdroidserver.metadata.parse_yaml_metadata(mf), dict())
def test_parse_yaml_metadata_empty_string_file(self):
mf = io.StringIO('""')
mf.name = 'mock_filename.yaml'
self.assertEqual(fdroidserver.metadata.parse_yaml_metadata(mf), dict())
def test_parse_yaml_metadata_unknown_app_field(self): def test_parse_yaml_metadata_unknown_app_field(self):
mf = io.StringIO( mf = io.StringIO(
textwrap.dedent( textwrap.dedent(
@ -375,7 +390,7 @@ class MetadataTest(unittest.TestCase):
mf.name = 'mock_filename.yaml' mf.name = 'mock_filename.yaml'
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
with self.assertRaises(MetaDataException): with self.assertRaises(MetaDataException):
fdroidserver.metadata.parse_yaml_metadata(mf, {}) fdroidserver.metadata.parse_yaml_metadata(mf)
def test_parse_yaml_metadata_unknown_build_flag(self): def test_parse_yaml_metadata_unknown_build_flag(self):
mf = io.StringIO( mf = io.StringIO(
@ -390,7 +405,7 @@ class MetadataTest(unittest.TestCase):
mf.name = 'mock_filename.yaml' mf.name = 'mock_filename.yaml'
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
with self.assertRaises(MetaDataException): with self.assertRaises(MetaDataException):
fdroidserver.metadata.parse_yaml_metadata(mf, {}) fdroidserver.metadata.parse_yaml_metadata(mf)
def test_parse_yaml_srclib_corrupt_file(self): def test_parse_yaml_srclib_corrupt_file(self):
with tempfile.TemporaryDirectory() as testdir: with tempfile.TemporaryDirectory() as testdir:
@ -487,9 +502,8 @@ class MetadataTest(unittest.TestCase):
) )
mf.name = 'mock_filename.yaml' mf.name = 'mock_filename.yaml'
mf.seek(0) mf.seek(0)
result = {}
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
fdroidserver.metadata.parse_yaml_metadata(mf, result) result = fdroidserver.metadata.parse_yaml_metadata(mf)
self.maxDiff = None self.maxDiff = None
self.assertDictEqual( self.assertDictEqual(
result, result,
@ -543,9 +557,8 @@ class MetadataTest(unittest.TestCase):
) )
mf.name = 'mock_filename.yaml' mf.name = 'mock_filename.yaml'
mf.seek(0) mf.seek(0)
result = {}
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
fdroidserver.metadata.parse_yaml_metadata(mf, result) result = fdroidserver.metadata.parse_yaml_metadata(mf)
self.maxDiff = None self.maxDiff = None
self.assertDictEqual( self.assertDictEqual(
result, result,
@ -594,9 +607,8 @@ class MetadataTest(unittest.TestCase):
) )
mf.name = 'mock_filename.yaml' mf.name = 'mock_filename.yaml'
mf.seek(0) mf.seek(0)
result = {}
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
fdroidserver.metadata.parse_yaml_metadata(mf, result) result = fdroidserver.metadata.parse_yaml_metadata(mf)
self.assertDictEqual( self.assertDictEqual(
result, result,
{ {
@ -629,9 +641,8 @@ class MetadataTest(unittest.TestCase):
) )
mf.name = 'mock_filename.yaml' mf.name = 'mock_filename.yaml'
mf.seek(0) mf.seek(0)
result = {}
with mock.patch('fdroidserver.metadata.warnings_action', 'error'): with mock.patch('fdroidserver.metadata.warnings_action', 'error'):
fdroidserver.metadata.parse_yaml_metadata(mf, result) result = fdroidserver.metadata.parse_yaml_metadata(mf)
self.assertNotIn('Provides', result) self.assertNotIn('Provides', result)
self.assertNotIn('provides', result) self.assertNotIn('provides', result)