Skip to content

Commit

Permalink
appstream: extract appstream metadata from parts
Browse files Browse the repository at this point in the history
Only supporting the summary and description right now.

Resolve canonical#1694

Signed-off-by: Kyle Fazzari <kyrofa@ubuntu.com>
  • Loading branch information
kyrofa committed Dec 21, 2017
1 parent 689f85c commit 28aecfc
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 3 deletions.
16 changes: 13 additions & 3 deletions snapcraft/extractors/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,19 @@
class UnhandledFileError(MetadataExtractionError):

fmt = (
"Failed to extract metadata from {file_path!r}: "
"Failed to extract metadata from {path!r}: "
"This file is not handled by {extractor_name!r}."
)

def __init__(self, file_path: str, extractor_name: str) -> None:
super().__init__(file_path=file_path, extractor_name=extractor_name)
def __init__(self, path: str, extractor_name: str) -> None:
super().__init__(path=file_path, extractor_name=extractor_name)


class AppstreamFileParseError(MetadataExtractionError):

fmt = (
"Failed to extract metadata from {path!r}: "
"it's not a valid XML file.")

def __init__(self, path: str) -> None:
super().__init__(path=path)
45 changes: 45 additions & 0 deletions snapcraft/extractors/appstream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright (C) 2017 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from ._metadata import ExtractedMetadata
from snapcraft.extractors import _errors

from xml.etree.ElementTree import (
ElementTree,
ParseError
)


def extract(path: str) -> ExtractedMetadata:
if not path.endswith('.metainfo.xml'):
raise _errors.UnhandledFileError(path, 'appstream')

try:
tree = ElementTree().parse(path)
except ParseError as e:
raise _errors.AppstreamFileParseError(path) from e

summary = None
node = tree.find('summary')
if node is not None:
summary = node.text

description = None
node = tree.find('description')
if node is not None:
description = node.text

return ExtractedMetadata(summary=summary, description=description)
78 changes: 78 additions & 0 deletions snapcraft/tests/integration/general/test_metadata_appstream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright (C) 2017 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os
import textwrap
import yaml

from testtools.matchers import Equals

from snapcraft.tests import (
fixture_setup,
integration
)


class AppstreamMetadataTestCase(integration.TestCase):

def setUp(self):
super().setUp()

with open('test.metainfo.xml', 'w') as appstream_file:
appstream_file.write(textwrap.dedent(
"""<?xml version="1.0" encoding="UTF-8"?>
<component>
<description>test-appstream-description</description>
<summary>test-appstream-summary</summary>
</component>"""))

def test_metadata_extracted_from_appstream(self):
snapcraft_yaml = fixture_setup.SnapcraftYaml(
self.path, summary=None, description=None)
snapcraft_yaml.data['adopt-info'] = 'test-part'
snapcraft_yaml.update_part(
'test-part', {
'plugin': 'dump',
'parse-info': ['test.metainfo.xml']})
self.useFixture(snapcraft_yaml)

self.run_snapcraft('prime')
with open(
os.path.join('prime', 'meta', 'snap.yaml')) as snap_yaml_file:
snap_yaml = yaml.load(snap_yaml_file)
self.assertThat(
snap_yaml['description'], Equals('test-appstream-description'))
self.assertThat(
snap_yaml['summary'], Equals('test-appstream-summary'))

def test_specified_metadata_not_overwritten(self):
snapcraft_yaml = fixture_setup.SnapcraftYaml(
self.path, description=None)
snapcraft_yaml.data['adopt-info'] = 'test-part'
snapcraft_yaml.update_part(
'test-part', {
'plugin': 'dump',
'parse-info': ['test.metainfo.xml']})
self.useFixture(snapcraft_yaml)

self.run_snapcraft('prime')
with open(
os.path.join('prime', 'meta', 'snap.yaml')) as snap_yaml_file:
snap_yaml = yaml.load(snap_yaml_file)
self.assertThat(
snap_yaml['description'], Equals('test-appstream-description'))
self.assertThat(
snap_yaml['summary'], Equals('test-summary'))
49 changes: 49 additions & 0 deletions snapcraft/tests/unit/extractors/test_appstream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright (C) 2017 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import textwrap

from snapcraft.extractors import appstream, ExtractedMetadata

from testtools.matchers import Equals

from snapcraft.tests import unit


class AppstreamTestCase(unit.TestCase):

scenarios = [
('summary', {
'key': 'summary',
}),
('description', {
'key': 'description',
}),
]

def test_appstream(self):
with open('foo.metainfo.xml', 'w') as f:
f.write(textwrap.dedent("""\
<?xml version="1.0" encoding="UTF-8"?>
<component>
<{key}>test-{key}</{key}>
</component>""".format(key=self.key)))

kwargs = {self.key: 'test-{}'.format(self.key)}
expected = ExtractedMetadata(**kwargs)

self.assertThat(
appstream.extract('foo.metainfo.xml'), Equals(expected))

0 comments on commit 28aecfc

Please sign in to comment.